Skip to main content

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, 3

Reliable ways to fix it

  1. 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 }
  1. 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); }
  1. 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); }
  1. Array methods (forEach, map) - the callback parameter is already "bound" to the value.
javascript
[0,1,2].forEach(i => setTimeout(() => console.log(i), 0));
  1. 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/const in loops - it is the simplest and safest way.
  • If you need var for some reason, fix the value via an IIFE/factory/argument pass.
  • Remember: closures capture the variable, not its instantaneous value - with var there is one variable for the whole loop.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.