for (var i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}1
2
3
4
4
4
varis function-scoped, not block-scoped.- The loop runs completely before the first
setTimeoutexecutes (becausesetTimeoutis asynchronous). - All the callbacks share the same
ivariable in the same lexical environment. - By the time the timeouts run,
iis 4 (loop has finished).
A closure is formed when the callback function inside setTimeout remembers the i variable from the outer scope.
In this case, all three callbacks close over the same variable i, not a separate copy for each iteration.
for (let i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}letcreates a newifor each iteration because it is block-scoped.
Output:
1
2
3
for (var i = 1; i <= 3; i++) {
(function (x) {
setTimeout(function () {
console.log(x);
}, x * 1000);
})(i);
}- The IIFE captures the current value of
iand passes it as a parameter (x), creating a new lexical environment for each loop.
for (var i = 1; i <= 3; i++) {
setTimeout(console.log.bind(null, i), i * 1000);
}bindcreates a new function withifixed as an argument.
"In the setTimeout + closure problem, var creates a single shared variable across iterations, so by the time the async callbacks run, the loop has already finished and the variable has the final value. This happens because all callbacks close over the same lexical environment. The fix is to create a new lexical scope for each iteration using let, IIFE, or .bind()."