JavaScript & TypeScript DISCUSSION

var vs let in a JavaScript loop: why do all my setTimeout callbacks print the same number?

Started by dattakakade var vs letJavaScript closuresblock scopehoistingtemporal dead zone
4 replies 248 views 5 participants
Latest activity · 30 Sep 2026

var vs let in a JavaScript loop: why do all my setTimeout callbacks print the same number?

dattakakade JavaScript & TypeScript Forum
#1

This loop is supposed to blink five LEDs in sequence in a browser simulation: for (var i = 0; i < 5; i++) { setTimeout(function () { toggle(i); }, i * 200); }. Instead toggle is called five times with 5. Changing var to let fixes it and I get 0 to 4, but I do not understand why one keyword changes what the callbacks see.

What is the actual difference between var, let and const, and is there any reason to still use var?

Community replies 4

Re: var vs let in a JavaScript loop: why do all my setTimeout callbacks print the same number?

#2

A closure keeps a reference to the variable, not a snapshot of its value. var is function-scoped, so there is one i for the whole enclosing function, shared by all five callbacks. The loop runs to completion, leaving i at 5, before the first timer fires; even a 0 ms timeout only runs after the current code has finished. Every callback then reads 5.

let is block-scoped, and the for statement has a special rule for it: a fresh binding of i is created for each iteration, initialised from the previous one. Each callback closes over its own i. Without let, the value can be passed as an extra timer argument instead: setTimeout(toggle, i * 200, i).

Re: var vs let in a JavaScript loop: why do all my setTimeout callbacks print the same number?

#3

The other differences. Scope: a var declared inside an if or for body is visible throughout the function, while let and const exist only inside the nearest pair of braces. Hoisting: all three are hoisted, but a var is initialised to undefined on function entry, so reading it before its declaration quietly yields undefined. let and const are in the temporal dead zone until the declaration executes, and reading them earlier throws a ReferenceError.

Redeclaration: var x; var x; is allowed, whereas repeating a let is a SyntaxError. Global object: a top-level var in a classic script becomes a property of window; let and const do not.

Re: var vs let in a JavaScript loop: why do all my setTimeout callbacks print the same number?

#4

const has the same scoping as let, must be initialised and cannot be reassigned. It does not make the value immutable: const cfg = { baud: 9600 }; cfg.baud = 115200; is fine, while cfg = {} throws a TypeError. Object.freeze(cfg) freezes the object itself, one level deep.

The common convention is const by default and let only for variables that are reassigned, such as counters and accumulators, so that a let tells the reader the value changes. In loops, for (const item of items) works because each iteration gets a new binding, but for (const i = 0; i < 5; i++) fails at the first i++.

Re: var vs let in a JavaScript loop: why do all my setTimeout callbacks print the same number?

#5

There is no good reason to use var in new code. All current browsers and Node.js versions support let and const, a transpiler can rewrite them for a very old engine, and the ESLint rules no-var and prefer-const enforce the habit.

Be aware that the sharing problem can return with let if it is declared outside the loop: let i; for (i = 0; i < 5; i++) { setTimeout(() => toggle(i), i * 200); } gives five 5s again, because there is only one binding. The per-iteration copy only happens when the let is in the loop header. In a while loop, declare a const n = i; inside the body for the callback to capture. For reference, your corrected loop fires its callbacks roughly 0, 200, 400, 600 and 800 ms after it ends.

TEP COMMUNITY