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?
That is because, in JavaScript,
varhas function scope.vardeclarations will be hoisted up to the top of the current execution context. That is, if it is inside of a function, thevarwill be the scoped inside the function’s execution context, otherwise the program (global) execution context.ECMAScript 2015 (a.k.a. ES6) introduces
letwhich 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
varand have it “scoped” inside the loop, is to create a new execution context, also know as closure:As I scoped both
iandjinside the callback scope, they will return the same values inside thesetTimeoutthan they had when they were passed tocallbackFactory.See Live demo.
Another way to do the same thing is to create an IIFE inside the
forloop. 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.Above I’ve created a new execution context inside the
forin each iteration. (Demo)Mixing the first approach (
callbackFactory) with the IIFE above, we could even make a 3rd option:This is simply using an IIFE in the place of the
callbackFactoryfunction. This doesn’t seem very easy to read and still creates functions inside theforloop 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
callbackFactoryin the same scope as theforloop, then instead of scoping theiinside of it, let the scope chain seek theiof the outer scope:Fiddle
Note that I’ve moved the
iandjdeclarations to the top of the scope solely for readability. It has the same effect asfor (var i = [...], which would be hoisted up by the interpreter.