Is there a way for javascript to detect all assigned variables? For example, if one js file creates a bunch of vars (globally scoped), can a subsequent file get all the vars without knowing what they're named and which might exist?
Thanks in advance :)
EDIT, Question Part 2:
How do I get the values of these variables? Here is what I have attempted:
This is what I ended up with, as per comment suggestions:
for (var name in this) {
variables[name] = name;
variables[name]=this[name]
}
Flanagan's "JavaScript - The Definitive Guide" gives the following on page 653:
var variables = ""
for (var name in this)
variables += name + "\n";
For Firefox, you can see the DOM tab -- easy, though not an answer to your question.
The for in loop provided in Kinopiko's answer will work, but not in IE. More is explained in the article linked below.
For IE, use the RuntimeObject.
if(this.RuntimeObject){
void function() {
var ro = RuntimeObject(),
results = [],
prop;
for(prop in ro) {
results.push(prop);
}
alert("leaked:\n" + results.join("\n"));
}();
}
See also:
Detecting Global Pollution with the JScript RuntimeObject (DHTML Kitchen article)
RuntimeObject (MSDN docs)
There is the this variable. This is an object or an array, and you can simply put:
for(i in this) { //do something }
Unfortunately, it will return everything under the this object.
This will output all the variables into the console without needing to read the variable yourself.
var variables = ""
for (var name in this)
variables += name + "\n";
console.log(variables)
/*
This could work too... but it's such a big unecessary code for something you could do in one line
var split = variables.split("\n");
for (var i in split)
console.log(split[i])
*/
If you want to assign values from one object to another, there are a couple of ways to do this:
//Way 1st
const variables= { ...this };
// or (I don't know what's the difference ;) )
// Don't forget const variables= {};
Object.assign(variables, this);
// Yes. It's very easy. You just "copy" entries from this to variables. I want to note that you are not copying a link to this, namely ENTRY.
Or
// Way 2nd. If u need to do smth. with entries.
const variables= [];
for (const name of Object.keys(this)) {
/*
Doing smth........
*/
variables[name] = this[name];
}
I want to note that this is not a way to collect all declared variables into an object (I am looking for this method myself). They are simply ways of copying the contents of one object into another.
Related
I have one module called functionalUtilities which contains a number of utility functions. An abbreviated version looks something like this:
MYAPP.functionalUtilities = (function() {
function map(func, array) {
var len = array.length;
var result = new Array(len);
for (var i = 0; i < len; i++)
result[i] = func(array[i]);
return result;
}
return {
map:map,
};
})();
I then have a second module which contains core code:
MYAPP.main = (function() {
//Dependencies
var f = MYAPP.functiionalUtilities;
//Do stuff using dependencies
f.map(...)
})()
It seems messy and annoying having to remember to type f.map each time I want to use map. Of course, in the dependencies, I could go though each of my functionalUtilities typing:
var map = f.map,
forEach = f.forEach,
etc.
but I wondered whether there is a better way of doing this? A lot of articles on namespacing that I've read mention aliasing, but don't suggest a way to sort of 'import all of the contents of an object into scope'.
Thanks very much for any help,
Robin
[edit] Just to clarify, I would like to use my functional utilities (map etc) within MYAPP.main without having to preface them every time with f.
This is possible by going through each function in MYAPP.functionalUtilities and assigning to a locally scoped variable within MYAPP.main. But the amount of code this requires doesn't justify the benefit, and it's not a general solution.
As I said in the comment. There is no real way of automatically defining local variables out of object properties. The only thing that comes to my mind is using eval:
for (var i in MYAPP.functiionalUtilities) {
eval("var " + i + " = MYAPP.functiionalUtilities[i];");
}
But I wouldn't use this method, since you could have object properties with strings as keys like this:
var obj = {
"my prop": 1
};
"my prop" might be a valid key for an object property but it's not a valid identifier. So I suggest to just write f.prop or define your local variables manually with var prop = f.prop;
EDIT
As Felix Kling mentioned in the comment section, there is in fact another way of achieving this, using the with statement, which I don't really know much about except for that it is deprectated.
Here's a late answer - I feel like adding to basilikum's answer.
1) The with keyword could be useful here!
with(MYAPP.functiionalUtilities) {
map(console.log, [ 'this', 'sorta', 'works', 'quite', 'nicely!' ]);
// Directly reference any properties within MYAPP.functiionalUtilities here!!
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
The with keyword is, in some ways, intended for exactly this situation. It should, of course, be noted that the mozilla developer link discourages use of with, and with is also forbidden in strict mode. Another issue is that the with statement causes its parameter to become the head of the scope chain, which means that it will always be checked first for all identifiers for all statements within the with block. This could be a performance hit.
2) An improvement to basilikum's answer
While a function call cannot add items to its parent-frame's scope, there is a way to avoid typing out a for-loop each time you wish to add a list of items to a namespace.
// First, define a multi-use function we can use each time
// This function returns a string that can be eval'd to import all properties.
var import = function(module) {
var statements = [];
for (var k in module) statements.push('var ' + i + ' = module["' + i + '"]');
return statements.join(';');
};
// Now, each time a module needs to be imported, just eval the result of import
eval(import(MYAPP.functiionalUtilities));
map(console.log, [ 'this', 'works!' ]);
The idea here is to replace the need to write a for-loop with something like eval(import(MYAPP.functiionalUtilities));.
The danger here, as basilikum has stated, is that module properties need to be valid identifier names.
I've some functions, stored in a collection/array and would like to get the key (function-name) without retyping it. Is there any short way to access it?
var functions_collection = {
"function_x": function() {
var name = "function_x";
// name = this.key; <- how to get the key/function-name "function_x"?
// some more code like:
$(".function_x .button").val();
alert(name);
}
}
Edit: I'd like to avoid retyping the "function_x" inside the function itself and prefer to call it like this.key.
Sorry for the weird topic and thanks in advance!
Solution: A lot of good answers, but I was just looking for this snipped:
Object.keys(this)
I'm not sure it's what you want but you can do this :
var functions_collection = {};
(function(name){
functions_collection[name] = function(){
// use name, which is the function
alert(name);
};
})("function_x");
I'm not really sure it's better. But depending on your (unspecified) goal, there's probably a better solution.
To get the name of the objects keys, you can use Object.getOwnPropertyNames(this) or in newer browsers just Object.keys(this), and that will get you an array of all and any keys the this object has :
var functions_collection = {
function_x: function() {
var name = Object.keys(this);
console.log(name);
}
}
FIDDLE
In my opinion you´d need to change you above code since you are having anonymous functions which have no name - a change like this should work:
var functions_collection = {
'function_x' : function function_x () {
var myName = arguments.callee.name;
alert(myName);
}
}
see http://jsfiddle.net/9cN5q/1/
There are several ways you could go here. Some are good ideas, some are not.
First, some bad ideas
Bad idea: arguments.callee.name
This translates most directly to what you ask. arguments.callee is
a reference to the function you're currently in. However, it's
considered bad
practice,
and you should avoid using it unless you have a really good reason.
Bad idea: Currying
After constructing the function, bind its own name into it as a parameter:
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
for (var name in functions_collection) {
if (typeof functions_collection[name] === "function") {
functions_collection[name] =
functions_collection[name].bind(functions_collection, name);
}
}
Currying is useful for lots of things in JavaScript, and it's a great idea in many situations. Not here, though, and I'll explain why below.
Bad idea: Use a local parameter and iterate through the containing object
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
for (var name in functions_collection) {
if (typeof functions_collection[name] === "function") {
functions_collection[name](name);
}
}
Of course, the obvious problem with this one is that you might not want to call every function in the collection at once. The more fundamental problem is that it continues the trend of dangerously tight coupling. This is a bad thing, potentially a Very Bad Thing that will cost you all kinds of headaches down the line.
Now the "right" way
Change your whole approach. Forget trying to recycle class names from your HTML; just keep it simple.
Good idea: Use a local variable
Who cares what you name your functions? If you know which HTML classes you want them to touch, just code them that way.
var functions_collection = {
"function_x": function() {
var name = "function_x"; //or "button" or any other class name
alert(name);
},
//more functions
};
functions_collection.function_x();
Good idea: Pass a parameter
You're already calling the function, right? So there's probably already code somewhere with access to the name you want.
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
functions_collection.function_x("function_x"); //or any other class name
Now you can use function_x on any class in your HTML, even if it doesn't match the function name:
functions_collection.function_x("function_y");
functions_collection.function_x("class_z");
functions_collection.function_x("button");
I've saved the simplest for last because I think you're making a mistake by trying to be "clever", if that makes sense. There are significant risks in your approach, and the payoff isn't going to be worth it.
Why the bad ideas are bad and the good ideas are good
Other than the arguments.callee.name option, the reason 2 and 3 are bad in this case is tight coupling. You're coupling function_x to the structure of functions_collection; you're coupling behavior to a variable name; and worst of all, you're coupling JS variables to the class names of HTML elements. This will make your code extremely fragile, and when you want to change something (and you will), get ready for a world of hurt.
For example, what happens if you reorganize your HTML? The page probably breaks, since the structure of your JS has to match the classes in your HTML/CSS. You'll have to rename or rewrite functions_collection and all others like it, or else you'll have to carefully plan new HTML around the JS you already have.
What happens if you want to use a JS minifier? Depends, but if you allow it to change member names in object literals, it completely breaks everything and you have to start over with one of the "good" ideas.
Now, what do you get in exchange for this inflexibility? You save an extra line at the beginning of each function. Not worth it, IMHO. Just bite the bullet and keep it simple.
Supposing that the variable name has the same name as its containing function:
var keys = [];
for (var p in functions_collection) {
if (typeof(functions_collection[p]) == 'function') {
keys.push(p);
}
}
And there you have it, an array with all the function names.
On (document).ready im would like to dynamically generate elements inside a certain parent-element. Lets call them "Candles".
Each "Candle" needs different properties for backgroundImage and color depending on their index().
After creating the page, these attributes need to be changeable via the interface. So its important to save the properties of the "candles" independent from each other.
Thats why I thought it might be useful, to generate for an object for each "Candle" to save their individual properties and to make them editable.
var candleAmount = 3;
for (var i=1; i <= candleAmount; i++) {
$("#container #candles").append("<li><img src=''></img></li>");
var Candle+i = [ "background":"+i+", "color":" - random - (ignore)" ]
};
(please dont mind any failures in the code besides the "Candle+i", I'll figure it out.)
EDIT: Ok, thank you so far. I might not made myself clear enaugh.
Here is an way more reduced example:
$("ul#candles li").each( function(i) {
candle+i = i+" Anything";
});
alert(candle4);
I would love to create an amount of variables depending on the Amount of child-objects.
What would be the correct syntax, or isn't there any?
Thank you
just put them in an array and access them via index. Result is most likely the same for you and is much better than let them floating in your scope
So is there any way, to generate object-names with an index?
Yes, in JavaScript, global variables are defined as properties of the global-object. (Inside a function, variables are defined as properties of the Activation object.) You can reference the global object by window (for browser applications) or just by this.
And because all objects are associative you can give there properties just the name you want. So, setting a global variable is equal to set a property into the global object.
var foo = "bar"; === this["foo"] = "bar";
Now, its just a small step to add a dynamic part to the name:
for(var i=0;i<10;i++) {
this['candle' + i] = i;
}
alert(candle7);
For your specific code:
$("ul#candles li").each( function(i) {
window["candle" + i] = i+" Anything";
});
alert(candle4);
I've seen something similar to this code in the Google API JavaScript, I mean the r=Array part. Here is an example of what they have done:
var r = Array;
var t = new r('sdsd' , 'sdsd');
alert(t[0]);
Few questions about this:
Is it legal to write like this and won't cause any problems?
I can do something similar with other keywords like ´For´ loop or with the ´this´ keyword?
Can I have article about this JavaScript official keyword shortcuts etc..?
Thank you in advance.
That works because Array is an object. You can do that with any object. For example, the Date object:
var d = Date;
console.log((new d()).getTime()); //Prints time
You cannot do that for keywords such as for or while because they are language constructs that will be recognised by the interpreter.
You can do it with this:
document.getElementById("b").onclick = function() {
var x = this; //this holds a reference to the DOM element that was clicked
x.value = "Clicked!";
}
In fact, that can be very useful sometimes (to keep a reference to this so you can access it from an anonymous inner function for example). This also works because, to put it simply, this will be a reference to an object.
Yes
for - no. this - yes.
You can store references to any JavaScript object in a variable. String, Array, Object, etc. are JavaScript objects that are built-in to the language. for, if, while, etc. are are JavaScript statements, and cannot be stored or referenced any other way.
You can do it the other way around as well (and really mess yourself up in the process):
Array = 0;
var myArray = new Array("a", "b", "c"); // throws error
This is easily undone like this:
Array = [].constructor;
Edit: Being able to assign the value of this to a variable is essential when nesting functions that will execute in a different scope:
function Widget() {
var that = this;
this.IsThis = function() {
return isThis();
};
function isThis() {
return that == this;
}
}
new Widget().IsThis(); // false!
Maybe not the best example, but illustrates losing scope.
You cannot reassign the value of this:
function doSomething() {
this = 0; // throws error
}
Is there is any way/tool to detect the duplicated variables/methods names in the project JavaScript files?
There is no such thing as duplicate names in Javascript. You will never get an error when re-declaring a name that already exists.
To avoid overwriting existing names in Javascript, good developers do at least one of these things:
1) Carefully keep their variables out of the global scope, usually adding all names needed for the app under just one or two globally-scoped objects.
// No
var foo = 1;
var bar = 2;
var bas = 3;
// Yes
var MyApp = {
foo:1,
bar:2,
bas:3
}
2) Check that a variable name does not yet exist before creating it.
// No
var MyObj = {};
// Yes
var MyObj = MyObj || {} // Use MyObj if it exists, else default to empty object.
jsLint might help you
http://www.jslint.com/
JavaScript Lint can probably help:
http://javascriptlint.com/
http://javascriptlint.com/