AceDevHub
Intermediate JavaScript Interview QuestionsIntermediateCode Output

JavaScript · Question 34

Why do callbacks created in a loop behave differently with var and let?

Direct answer

var creates one function-scoped loop binding shared by the callbacks, while let creates a fresh per-iteration binding, so delayed callbacks capture different values.

var-loop-closure.js
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
Output
3
3
3
let-loop-closure.js
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
Output
0
1
2

Consider for (var i=0;i<3;i++){ setTimeout(() => console.log(i),0); }. By the time the callbacks run, the loop has completed and the single shared i binding contains 3, so the callbacks log 3, 3, 3.

With for (let i=0;i<3;i++){ setTimeout(() => console.log(i),0); }, each iteration gets its own lexical binding. The callbacks therefore observe 0, 1, 2. The difference is about bindings and closures; it is not that setTimeout “copies” the value.

  • With modern JavaScript, prefer let when the loop variable should be captured per iteration.
  • Before block-scoped bindings were available, developers commonly used an IIFE or another function call to create a new scope for each iteration.
  • The timer delay is not the core issue; any callback that executes later can reveal the same closure behavior.