Reputation: 713
Heres a sample bit of code, similar to that that gave me grief:
for(var i=0;i<3;i++){
var Proof = true
if (i == 0){
Proof = false
//var CallBack = function(){alert(Proof);}; // true????
var CallBack = function(){alert(i);};
}
}
// Whatever happened to local scope? shouldn't CallBack be undefined now?
CallBack()// alert(3), should be 0?
CallBack isn't undefined, it alerts true or 3, could someone please explain to me what is going on that is making it act like this? is there some local keyword I am missing, or does JS not have scope? I am using no framework, and would appreciate some help on how to sort this out, and do this kind of thing properly...
Upvotes: 1
Views: 96
Reputation: 23267
When CallBack is being called, for
loop is finished already. So i
equals 3.
If you want i
to be local, you should write like this:
var CallBack;
for(var i=0;i<3;i++){
(function(index) {
var Proof = true
if (index == 0){
Proof = false
//var CallBack = function(){alert(Proof);}; // true????
CallBack = function(){alert(index);};
}
})(i);
}
CallBack()
Here is a demo
UPDATE
Here is an alternative:
var CallBack;
for(var i=0;i<3;i++){
var Proof = true
if (i == 0){
Proof = false
CallBack = (function(index) {
return function(){alert(index);};
})(i);
}
}
CallBack()
UPDATE 2
EXPLAINATION:
By writing (function(index) {...})(i)
we declare an anonymous function and call it immediately (we could also write function(index) {...}(i)
but I think the first case is clearer). In javascript, function
is also a way to set another scope of variables, so in our case we "remember" the current state of i
variable in index
variable that is visible only within our anonymous function.
And when the 'CallBack' is called, alert
shows 0
because i
was equal 0
when that function was called.
Upvotes: 1
Reputation: 1074138
JavaScript doesn't have block scope at all (yet, see below). All variables are declared throughout the function in which they appear (or throughout the global scope, if they're at global scope).
Assuming the code you've quoted is the only code in its scope, it's exactly the same as this:
var i;
var Proof;
var Callback;
for(i=0;i<3;i++){
Proof = true
if (i == 0){
Proof = false
//CallBack = function(){alert(Proof);}; // true????
CallBack = function(){alert(i);};
}
}
// Whatever happened to local scope? shouldn't CallBack be undefined now?
CallBack()// alert(3), should be 0?
More (on my blog): Poor, misunderstood var
CallBack()// alert(3), should be 0?
No, 3
is correct, because that's the value of i
as of when CallBack
is called. CallBack
is a closure over the context in which it was created. It has an enduring reference to the context (including the i
variable), not a copy of what existed when it was created.
If you wanted to get 0
instead, you'd have to have CallBack
close over something other than i
, something that won't change. The typical way to do that is to use a builder function you pass a value into:
function buildCallBack(index) {
return function() { alert(index); };
}
Then:
CallBack = buildCallBack(i);
The value of i
is passed into buildCallBack
as the argument index
. buildCallBack
creates a function closing over the context of that call to buildCallBack
and using that index
argument. Since that argument's value is never changed, the callback alerts 0
(in this case) rather than 3
.
More (on my blog): Closures are not complicated
The reason I said "yet" above is that the next version of the spec (ES6) will introduce new block-scope semantics for the new let
and const
keywords and block function declarations. var
will be unchanged, but if you use let
to declare a variable instead, it has block scope rather than function/global scope.
Upvotes: 2