Mistake with var in closures and loop
Classic case: in a loop with var, all callbacks "see" the same variable i (var has function scope). By the time the callbacks run, i already equals the final value of the loop.
The problem
javascript
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// 3, 3, 3Reliable ways to fix it
- Use
let/const(ES6+) - the loop gets a new lexical binding on every iteration.
javascript
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 0, 1, 2
}- IIFE (immediately invoked function expression) - "freeze" the current value of
i.
javascript
for (var i = 0; i < 3; i++) {
(function(iCopy) {
setTimeout(() => console.log(iCopy), 0);
})(i);
}- Pass arguments to the callback through a wrapper/factory
javascript
function makeLogger(x) {
return () => console.log(x);
}
for (var i = 0; i < 3; i++) {
setTimeout(makeLogger(i), 0);
}- Array methods (
forEach,map) - the callback parameter is already "bound" to the value.
javascript
[0,1,2].forEach(i => setTimeout(() => console.log(i), 0));- Pass an argument into
setTimeout(supported in browsers/Node):
javascript
for (var i = 0; i < 3; i++) {
setTimeout(x => console.log(x), 0, i);
}Quick checklist
- By default use
let/constin loops - it is the simplest and safest way. - If you need
varfor some reason, fix the value via an IIFE/factory/argument pass. - Remember: closures capture the variable, not its instantaneous value - with
varthere is one variable for the whole loop.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.