CodePark/ questions / Closures
START PRACTICINGPRACTICE
Frontend question · JavaScript › Closures

The loop for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) logs 3 3 3. Why, and what are the ways to fix it?

There is only one i, because var is function-scoped. All three arrow functions capture that same variable, not its value. They run after the loop has finished, when i is 3.

There are three ways to fix it:

1. Use let (the modern fix). A for loop with let creates a new binding of i for each iteration. A binding is the link between a name and its value. So each callback captures its own i.

2. Create a new function scope per iteration (the pre-ES6 fix). An IIFE (a function that runs immediately) does this: (function (j) { setTimeout(() => console.log(j)); })(i). The parameter j is a new copy for each call.

3. Pass the value as an argument. Use setTimeout(console.log, 0, i) or setTimeout(fn.bind(null, i)). These capture the current value of i, not the variable.

  • var gives one shared i. Closures capture variables, not values.
  • The callbacks run only after the loop has finished.
  • let creates a new binding of i for each iteration.
  • Older fixes use an IIFE or bind to save the current value.