Question 13 / 14

Why does swapping var for let fix the setTimeout loop?

Topic: Closures

let creates a fresh binding for i on every iteration, so each callback closes over its own private value.

Solve it with the execution visualizerStep through every binding, function call, and console output below.

Predict the output

Predict the output. Now that the loop uses let, does each callback still read the same final i?

for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 100);
}
Reveal the answer and explanation

Expected output

0
1
2

Why this happens

A for loop with let creates a brand-new i binding for every iteration, initialized by copying the value from the previous iteration.

Each call to setTimeout registers a callback that closes over that specific iteration’s binding, not a single shared one, so the three callbacks capture three separate environments.

When the timer queue drains after the loop finishes, each callback looks up i in its own private binding and logs the value it was created with: 0, then 1, then 2.

What this question tests

  • let is block-scoped, with a new binding per iteration
  • closures over different bindings stay independent
  • contrast with the var version, where every callback shares one binding

Runtime assumptions

The visualizer models each `let` iteration as its own lexical environment, matching how per-iteration bindings work in Node.js, browsers, and other standard JavaScript runtimes.

Specification references

JavaScript Code EditorDirectly edit or paste custom code
Step 0 of 0
StepSpaceRun
Press → (Step) or Space (Run)
Phase 1: Compilation(Hoisting & Memory Setup)
Phase 2: Execution(Line-by-line Evaluation)
Execution Step Breakdown

Press "→" / click "▶" to step line-by-line, or press "Space" / click "▶ Run" for auto-play.

Scope Chain
Lexical Environment Chain: Inner Scope → Outer [[Scope]] → Global (GO)
No active scope chain
CALL STACKStack of Execution Contexts (LIFO)
LIFO Stack Frames
Global Execution Context (GEC)
▶ Active Frame
HEAP & MEMORY SPACEAllocated Objects (GO, AO, FunctionObjects, ER)
ECMAScript Heap Area
No objects in memory
Console Output
console.log(...) Stream
No output generated yet