Hiprup

What is the output of the var loop with setTimeout?

A classic closure gotcha: a loop using var with setTimeout logs the final value repeatedly, not 0, 1, 2…

  • Why — var is function-scoped, so all callbacks share one variable. By the time the timers fire, the loop has finished and the variable holds its final value.

  • Fix with let — block-scoped, so each iteration gets its own copy and you get 0, 1, 2…

  • Fix with an IIFE — capture the current value in a new scope per iteration.

Root cause: closures capture variables by reference, not by the value at creation time.

// BUG: var + setTimeout
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3 (all callbacks see the final i)

// FIX 1: let (new binding per iteration)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2 ✓

// FIX 2: IIFE (new scope per iteration)
for (var i = 0; i < 3; i++) {
  ((j) => {
    setTimeout(() => console.log(j), 100);
  })(i);
}
// Output: 0, 1, 2 ✓

// FIX 3: setTimeout third argument
for (var i = 0; i < 3; i++) {
  setTimeout((j) => console.log(j), 100, i); // i passed as arg
}
// Output: 0, 1, 2 ✓

var i: one variable shared across iterations. When callbacks run (after loop ends), i = 3. let i: new variable per iteration — each callback captures its own copy.

IIFE creates a new scope with j = current i. setTimeout's third argument passes the current i value directly to the callback parameter.

This is the #1 most asked JavaScript gotcha. Explain WHY (var is function-scoped, one shared variable).

Show THREE fixes: let (modern, recommended), IIFE (classic), and setTimeout third argument (lesser known). The let fix is one character change that demonstrates understanding.