Коротко — почему так: `var` имеет функциональную область, цикл завершается до выполнения колбэков, поэтому к моменту вызовов `i` равно 3 \;3\;3. В результате в консоль выводится 3 \;3\;3 три раза. Способы получить ожидаемый вывод 0,1,2 \;0,1,2\;0,1,2: 1) Блоковая переменная `let`: for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } 2) IIFE (замыкание на параметре): for (var i = 0; i < 3; i++) { (function (j) { setTimeout(() => console.log(j), 0); })(i); } 3) Фабрика функций (closure): function makeLogger(x) { return () => console.log(x); } for (var i = 0; i < 3; i++) { setTimeout(makeLogger(i), 0); } 4) Передача аргумента в `setTimeout` (поддерживается в среде): for (var i = 0; i < 3; i++) { setTimeout((j) => console.log(j), 0, i); } 5) `bind`: for (var i = 0; i < 3; i++) { setTimeout(console.log.bind(null, i), 0); } Во всех вариантах идея одна: замкнуть текущее значение счётчика в отдельной области/аргументе, а не ссылаться на общую `var i`, которая после цикла равна 3 \;3\;3.
Способы получить ожидаемый вывод 0,1,2 \;0,1,2\;0,1,2:
1) Блоковая переменная `let`:
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
2) IIFE (замыкание на параметре):
for (var i = 0; i < 3; i++) {
(function (j) {
setTimeout(() => console.log(j), 0);
})(i);
}
3) Фабрика функций (closure):
function makeLogger(x) { return () => console.log(x); }
for (var i = 0; i < 3; i++) {
setTimeout(makeLogger(i), 0);
}
4) Передача аргумента в `setTimeout` (поддерживается в среде):
for (var i = 0; i < 3; i++) {
setTimeout((j) => console.log(j), 0, i);
}
5) `bind`:
for (var i = 0; i < 3; i++) {
setTimeout(console.log.bind(null, i), 0);
}
Во всех вариантах идея одна: замкнуть текущее значение счётчика в отдельной области/аргументе, а не ссылаться на общую `var i`, которая после цикла равна 3 \;3\;3.