Scoping rules for variables initialized in a for loop

javascript, scoping, settimeout

Solution

That is because, in JavaScript, `var` has function scope.

`var` declarations will be hoisted up to the top of the current execution context. That is, if it is inside of a function, the `var` will be the scoped inside the function's execution context, otherwise the program (global) execution context.

ECMAScript 2015 (a.k.a. ES6) introduces `let` which lets you create block scope vars, but as it is not widely supported I'll just leave the link for reference.

An workaround, to still use `var` and have it "scoped" inside the loop, is to create a new execution context, also know as closure:

function callbackFactory(i, j) {
    // Now `i` and `j` are scoped inside each `callbackFactory` execution context.
    return function() { // This returned function will be used by the `setTimeout`.
       // Lexical scope (scope chain) will seek up the closest `i` and `j` in parent
       // scopes, that being of the `callbackFactory`'s scope in which this returned
       // function has been initialized.
       console.log("in timeout i is: " + i + " j is: " + j);
    };
}
for(var i = 0; i < 5; i++) {
    var j = i + 10;
    console.log("i is: " + i + " j is: " + j);
    setTimeout( callbackFactory(i, j), i * 1000);
}

As I scoped both `i` and `j` inside the callback scope, they will return the same values inside the `setTimeout` than they had when they were passed to `callbackFactory`.

See Live demo.

Another way to do the same thing is to create an IIFE inside the `for` loop. This is usually simpler to read but JS(H|L)int will yell at you. `;)` This is because creating functions inside a loop is considered bad for performance.

for(var i = 0; i < 5; i++) {
    var j = i + 10;
    console.log("i is: " + i + " j is: " + j);
    (function(i, j) { // new execution context created for each iteration
        setTimeout(function() {
            console.log("in timeout i is: " + i + " j is: " + j);
        }, i * 1000);
    }(i, j)); // the variables inside the `for` are passed to the IIFE
}

Above I've created a new execution context inside the `for` in each iteration. (Demo)

Mixing the first approach (`callbackFactory`) with the IIFE above, we could even make a 3rd option:

for(var i = 0; i < 5; i++) {
    var j = i + 10;
    console.log("i is: " + i + " j is: " + j);
    setTimeout(function(i, j) {
        return function() {
            console.log("in timeout i is: " + i + " j is: " + j);
        };
    }(i, j), i * 1000);
}

This is simply using an IIFE in the place of the `callbackFactory` function. This doesn't seem very easy to read and still creates functions inside the `for` loop which is bad for performance, but just noting that this is also possible and works.

These 3 approaches are very commonly seen in the wild. `=]`

Oh, almost forgot to answer the main question. Just put the `callbackFactory` in the same scope as the `for` loop, then instead of scoping the `i` inside of it, let the scope chain seek the `i` of the outer scope:

(function() {
    var i, j;
    function callbackFactory(j) {
    // the `j` inside this execution context enters it as a formal parameter,
    // shadowing the outer `j`. That is, it is independent from the outer `j`.
    // You could name the parameter as "k" and use "k" when logging, for example.
        return function() {
           // Scope chain will seek the closest `j` in parent scopes, that being
           // the one from the callbackFactory's scope in which this returned
           // function has been initialized.
           // It will also seek up the "closest" `i`,
           // which is scoped inside the outer wrapper IIFE.
           console.log("in timeout i is: " + i + " j is: " + j);
        };
    }
    for(i = 0; i < 5; i++) {
        j = i + 10;
        console.log("i is: " + i + " j is: " + j);
        setTimeout( callbackFactory(j), i * 1000);
    }
}());
/* Yields:
i is: 0 j is: 10  
i is: 1 j is: 11  
i is: 2 j is: 12  
i is: 3 j is: 13  
i is: 4 j is: 14  
in timeout i is: 5 j is: 10  
in timeout i is: 5 j is: 11  
in timeout i is: 5 j is: 12  
in timeout i is: 5 j is: 13  
in timeout i is: 5 j is: 14 */

Fiddle

Note that I've moved the `i` and `j` declarations to the top of the scope solely for readability. It has the same effect as `for (var i = [...]`, which would be hoisted up by the interpreter.

Problem

Possible Duplicate: Javascript closure inside loops - simple practical example I'm playing around with setTimeout in a project of mine in order to throttle the adding of elements to the DOM (so UI won't freeze during page loading). However, I've encountered something a bit puzzling to me. Given this code: ``` for(var i = 0; i < 5; i++) { var j = i + 10; console.log("i is: " + i + " j is: " + j); setTimeout(function() { console.log("in timeout i is: " + i + " j is: " + j); }, i * 1000); } ``` I get the following output: ``` i is: 0 j is: 10 i is: 1 j is: 11 i is: 2 j is: 12 i is: 3 j is: 13 i is: 4 j is: 14 in timeout i is: 5 j is: 14 in timeout i is: 5 j is: 14 in timeout i is: 5 j is: 14 in timeout i is: 5 j is: 14 in timeout i is: 5 j is: 14 ``` That the value of `i` in the timeout is 5 is obvious since i is scoped in the for loop initialization. However, how come `j` is 14 for all timeout outputs? I would have thought that `j` would have output 10, 11, 12, 13, 14 in the timeout since it is scoped within the loop. How could I achieve that result?

Original source

Related problems