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.

What is the output of the var loop with setTimeout? | Hiprup