Weeve Ferrelaine
Weeve Ferrelaine

Reputation: 713

How does Javascript's Scope work, getting unexpected behavior?

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

Answers (2)

Oleg
Oleg

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()

demo

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

T.J. Crowder
T.J. Crowder

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

Related Questions