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
letwhen 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.