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.