What is wrong with my javascript scope? [duplicate]

The following alerts 2 every time.

function timer() {
    for (var i = 0; i < 3; ++i) {
        var j = i;
        setTimeout(function () {
            alert(j);
        }, 1000);
    }
}

timer();

Shouldn't var j = i; set the j into the individual scope of the setTimeout?

Whereas if I do this:

function timer() {
    for (var i = 0; i < 3; ++i) {
        (function (j) {
            setTimeout(function () {
                alert(j);
            }, 1000);
        })(i);
    }
}

timer();

It alerts 0, 1, 2 like it should.

Is there something I am missing?


Javascript has function scope. This means that

for(...) {
    var j = i;
}

is equivalent to

var j;
for(...) {
    j = i;
}

In fact, this is how Javascript compilers will actually treat this code. And, of course, this causes your little "trick" to fail, because j will be incremented before the function in setTimeout gets called, i.e. j now doesn't really do anything different than i, it's just an alias with the same scope.

If Javascript were to have block scope, your trick would work, because j would be a new variable within every iteration.

What you need to do is create a new scope:

for(var i = ...) {
    (function (j) {
        // you can safely use j here now
        setTimeout(...);
    })(i);
}

The alternative the the IIFE is a function factory:

function timer() {
    for (var i = 0; i < 3; ++i) {
        setTimeout(createTimerCallback(i), 1000);
    }
}

function createTimerCallback(i) {
    return function() {
       alert(i);
    };
}

timer();

This being said, this is one of the most asked questions in the javascript tag. See:

  • JavaScript closure inside loops – simple practical example
  • Javascript infamous Loop issue?

An alternative is to use the (normally abused) keyword with:

function timer() {
    for (var i = 0; i < 3; ++i) {
        with({j: i}) {
            setTimeout(function () {
                alert(j);
            }, 1000);
        }
    }
}

timer();

It creates a new scope like functions do, but without the awkward syntax. I first saw it here: Are there legitimate uses for JavaScript's “with” statement?