In Python one can get a dictionary of all local and global variables in the current scope with the built-in functions locals() and globals(). Is there some equivalent way of doing this in Javascript? For instance, I would like to do something like the following:
var foo = function(){ alert('foo'); };
var bar = function(){ alert('bar'); };
var s = 'foo';
locals()[s](); // alerts 'foo'
Is this at all possible, or should I just be using a local object for the lookup?
locals() - No.
globals() - Yes.
window is a reference to the global scope, like globals() in python.
globals()["foo"]
is the same as:
window["foo"]
Well, I don't think that there is something like that in js. You can always use eval instead of locals(). Like this:
eval(s+"()");
You just have to know that actually function foo exists.
Edit:
Don't use eval:) Use:
var functionName="myFunctionName";
window[functionName]();
I seem to remember Brendan Eich commented on this in a recent podcast; if i recall correctly, it's not being considered, as it adds unreasonable restrictions to optimization. He compared it to the arguments local in that, while useful for varargs, its very existence removes the ability to guess at what a function will touch just by looking at its definition.
BTW: i believe JS did have support for accessing locals through the arguments local at one time - a quick search shows this has been deprecated though.
#e-bartek, I think that window[functionName] won't work if you in some closure, and the function name is local to that closure. For example:
function foo() {
var bar = function () {
alert('hello world');
};
var s = 'bar';
window[s](); // this won't work
}
In this case, s is 'bar', but the function 'bar' only exists inside the scope of the function 'foo'. It is not defined in the window scope.
Of course, this doesn't really answer the original question, I just wanted to chime in on this response. I don't believe there is a way to do what the original question asked.
#pkaeding
Yes, you're right. window[functionName]() doesn't work in this case, but eval does. If I needed something like this, I'd create my own object to keep those functions together.
var func = {};
func.bar = ...;
var s = "bar";
func[s]();
AFAIK, no. If you just want to check the existence of a given variable, you can do it by testing for it, something like this:
if (foo) foo();
Related
I can't seem to find a question that answers this directly.
I'd like to use a function argument as the name of a variable within that function.
e.g.,
test(var1);
function test(foo)
{
var foo = 'Hello world!'; // Set var1
}
alert(var1); // Hello world!
Can I use the brackets here (i.e., window.[ ])?
Yeah, you can use brackets:
window[foo] = "Hello World"
Here's a JSFiddle
Er...okay, so this is almost certainly not a good idea.
Short answer: sorta. If you're running in a browser, you can do this:
var polluteGlobalNamespace = function(symbol) {
window[symbol] = "whatever";
};
polluteGlobalNamespace('foo');
console.log(foo);
But that only works for global variables. There is no way to do this with function-scoped variables, because JavaScript lacks first class environments.
But unless you're doing some deep dark metaprogramming, this isn't a good idea. It might be better to post the problem that you're trying to solve with this function to see what the idiomatic way to do it is.
I have a 3rd party JavaScript application that declares several global functions and needs some variables to be defined for those functions to work. I would rather not define those variables in the global scope. Here's an example:
Suppose you have a globally defined function foo that prints out the value of an externally-defined variable named x.
function foo() {
console.log(x);
}
I think the only way for x to have a value within foo is if x is also a global. I hope I'm wrong. What I would really like to do is this:
(function () {
var x = 'someValue';
var bar = magicallyFixSoXIsntGlobal(foo);
bar();
}());
I guess I could do:
(function () {
var x = 'someValue';
var bar = Function(foo.toString());
bar();
}());
But that seems pretty much like eval. (If it comes down to that, I'd rather have global spam than use eval.)
Is there any way to "fix" the global functions so they can refer to the enclosed values?
Assign a value to a window's property, call the function, then use the delete operator to truly remove the variable.
window.foo = 'bar';
magic();
delete window.foo; // Because `foo` is defined without `var`, it can be deleted
The obvious solution is to modify foo to take x as a variable. But, assuming you can't / don't want to do that, I can think of two options:
declare x as a global variable.
Wrap foo's declaration in a closure.
The first will certainly make foo work, but please don't take Rob W's approach. Remember that the reason people tell you "don't clutter the global namespace" isn't because they're worried about unused variables hanging around (that can be a problem, but it's not specific to the global namespace). It's because having multiple functions using the same namespace increases the chances that one will overwrite the other's variables. Here's how you would use the global x without bludgeoning the rest of your code.
!function(x, exists) {
window.x=10;
foo();
if(exists)
window.x=x;
else
delete window.x;
}(window.x, 'x' in window);
The second option shouldn't be too hard if foo is currently just a function declaration within your code. In that case, change it to:
!function() {
var x=10;
function foo() {
[...]
}
// foo can only be called within this block
}();
Or if foo must be global, but you still don't want x to be global:
!function() {
var x=10;
window.foo=function() {
[...]
};
// now foo can be called anywhere, and will still use the x declared above
}();
But if foo is included via an external script then it gets more complicated, and I don't think you could use this method without modifying foo. (You could also use ajax and eval, or just eval, but I think you're right in avoiding that function.)
This is what function parameters are for
function foo(x) {
console.log(x)
}
You could hack it horrible if you wanted by converting the function to a string and creating a new function which accepts parameters, but that's dirty as hell
Basically what I am after is this,
var foo = 'I am foo!';
alert(window['foo']);
// output = I am foo!
This does not work in a jQuery wrapper, I understand that it may be due to jQuery having renamed the window object or whatnot. If anyone has any inkling as to if this may be possible in a jQuery wrapper, I would appreciate the knowledge !
foo = "I am foo!";
If you have a function scope around your var line, it'll define it as a local variable instead of a global variable. It's not jQuery "renaming the window object" or anything silly like that, it's just how var works. I'd personally doing
window.foo = "I am foo!";
// or
window["foo"] = "I am foo!";
though, to make it explicit you wanted to define a global variable. Or use a comment to say that.
jQuery doesn't rename the window object - jQuery is actually under it (window.jQuery).
Try this:
window.foo = 'I am foo!';
alert(window.foo);
For non global variables or private variables it's possible to use eval over string expressions;
var JohnDoe = function(foo){
var x;
eval("x = foo");
return x;
};
//now we can setup foo values
JohnDoe("MAKUKULA")
=> "MAKUKULA"
But it might be pointed out the following:
This feature is obsolete. Although it may still work in some browsers,
its use is discouraged since it could be removed at any time. Try to
avoid using it.
I've heard from a variety of places that global variables are inherently nasty and evil, but when doing some non-object oriented Javascript, I can't see how to avoid them. Say I have a function which generates a number using a complex algorithm using random numbers and stuff, but I need to keep using that particular number in some other function which is a callback or something and so can't be part of the same function.
If the originally generated number is a local variable, it won't be accessible from, there. If the functions were object methods, I could make the number a property but they're not and it seems somewhat overcomplicated to change the whole program structure to do this. Is a global variable really so bad?
I think your best bet here may be to define a single global-scoped variable, and dumping your variables there:
var MyApp = {}; // Globally scoped object
function foo(){
MyApp.color = 'green';
}
function bar(){
alert(MyApp.color); // Alerts 'green'
}
No one should yell at you for doing something like the above.
To make a variable calculated in function A visible in function B, you have three choices:
make it a global,
make it an object property, or
pass it as a parameter when calling B from A.
If your program is fairly small then globals are not so bad. Otherwise I would consider using the third method:
function A()
{
var rand_num = calculate_random_number();
B(rand_num);
}
function B(r)
{
use_rand_num(r);
}
Consider using namespaces:
(function() {
var local_var = 'foo';
global_var = 'bar'; // this.global_var and window.global_var also work
function local_function() {}
global_function = function() {};
})();
Both local_function and global_function have access to all local and global variables.
Edit: Another common pattern:
var ns = (function() {
// local stuff
function foo() {}
function bar() {}
function baz() {} // this one stays invisible
// stuff visible in namespace object
return {
foo : foo,
bar : bar
};
})();
The returned properties can now be accessed via the namespace object, e.g. ns.foo, while still retaining access to local definitions.
What you're looking for is technically known as currying.
function getMyCallback(randomValue)
{
return function(otherParam)
{
return randomValue * otherParam //or whatever it is you are doing.
}
}
var myCallback = getMyCallBack(getRand())
alert(myCallBack(1));
alert(myCallBack(2));
The above isn't exactly a curried function but it achieves the result of maintaining an existing value without adding variables to the global namespace or requiring some other object repository for it.
I found this to be extremely helpful in relation to the original question:
Return the value you wish to use in functionOne, then call functionOne within functionTwo, then place the result into a fresh var and reference this new var within functionTwo. This should enable you to use the var declared in functionOne, within functionTwo.
function functionOne() {
var variableThree = 3;
return variableThree;
}
function functionTwo() {
var variableOne = 1;
var var3 = functionOne();
var result = var3 - variableOne;
console.log(variableOne);
console.log(var3);
console.log('functional result: ' + result);
}
functionTwo();
If another function needs to use a variable you pass it to the function as an argument.
Also global variables are not inherently nasty and evil. As long as they are used properly there is no problem with them.
If there's a chance that you will reuse this code, then I would probably make the effort to go with an object-oriented perspective. Using the global namespace can be dangerous -- you run the risk of hard to find bugs due to variable names that get reused. Typically I start by using an object-oriented approach for anything more than a simple callback so that I don't have to do the re-write thing. Any time that you have a group of related functions in javascript, I think, it's a candidate for an object-oriented approach.
Another approach is one that I picked up from a Douglas Crockford forum post(http://bytes.com/topic/javascript/answers/512361-array-objects). Here it is...
Douglas Crockford wrote:
Jul 15 '06
"If you want to retrieve objects by id, then you should use an object, not an
array. Since functions are also objects, you could store the members in the
function itself."
function objFacility(id, name, adr, city, state, zip) {
return objFacility[id] = {
id: id,
name: name,
adr: adr,
city: city,
state: state,
zip: zip
}
}
objFacility('wlevine', 'Levine', '23 Skid Row', 'Springfield', 'Il', 10010);
"The object can be obtained with"
objFacility.wlevine
The objects properties are now accessable from within any other function.
I don't know specifics of your issue, but if the function needs the value then it can be a parameter passed through the call.
Globals are considered bad because globals state and multiple modifiers can create hard to follow code and strange errors. To many actors fiddling with something can create chaos.
You can completely control the execution of javascript functions (and pass variables between them) using custom jQuery events....I was told that this wasn't possible all over these forums, but I got something working that does exactly that (even using an ajax call).
Here's the answer (IMPORTANT: it's not the checked answer but rather the answer by me "Emile"):
How to get a variable returned across multiple functions - Javascript/jQuery
I have code like this
var MyObj = {
f1 : function(o){ o.onmousedown = MyObj.f2;},
f2 : function(){ MyObj.f1(); }
}
I would like to know how to refer to MyObj without hardcoding it (i.e. this), so I can change object name without changing the code.
I am interested only in litteral object notation.
EDIT:
I didn't get it right. One of the functions was actually onmousedown event, so this wasn't working in it... This will refer to the object that rised event. I wonder is it still possible to refer to MyObj in such case.
Use this.
Eg:
var MyObj = {
f1 : function(){ ... },
f2 : function(){ this.f1(); }}
One of the functions was actually
onmousedown event, so this wasn't
working in it... This will refer to
the object that rised event. I wonder
is it still possible to refer to MyObj
in such case.
One way follows, though it may not be very useful in this case.
var baz = (function(){ // fake a scope
var foo; // foo will exist in the next statement
foo = { bar: function(){ return foo } };
return foo;
})();
The function wrapper, which is executed immediately, acts to keep variables scoped in that block (later versions of JS by Mozilla have improved scoping syntax, but I'm assuming that's not an option). In this simple example, foo is returned and used as the value for baz, but if you were really doing an assignment and didn't care if foo stayed around, then you'd only need the second two lines. But if it were part of a larger expression, then this idiom might come in handy.