Коротко — потому что стрелочная функция замыкает не текущее значение переменной iii, а саму переменную (её привязку). К моменту выполнения всех отложенных функций цикл уже завершён и iii равно 555, поэтому все колбэки выводят 555. Пояснение по шагам - Hoisting и область видимости. Объявление `var i` поднимается (hoisted) в функцию/глобальную область: фактически есть одна общая переменная iii для всего цикла, а не новая для каждой итерации. В коде for (var i=000; i<555; i++){ setTimeout(()=>console.log(i),000); } после завершения цикла значение iii равно 555. - Замыкание. Функции, переданные в `setTimeout`, замыкают ссылку на переменную iii. Они не получают «снимок» значения во время создания — при вызове они читают текущее значение той же переменной iii, которое стало 555. Варианты исправления (печатают 0…40\ldots40…4) 1) Использовать `let` (блочная область видимости — новая привязка для каждой итерации): for (let i=000; i<555; i++){ setTimeout(()=>console.log(i),000); } 2) Замыкать текущее значение через IIFE (функция сразу вызывается и получает копию): for (var i=000; i<555; i++){ (function(x){ setTimeout(()=>console.log(x),000); })(i); } 3) Передать значение в `setTimeout` через аргументы (некоторые окружения поддерживают дополнительн. аргументы): for (var i=000; i<555; i++){ setTimeout(function(x){ console.log(x); }, 000, i); } 4) Использовать `.bind` чтобы зафиксировать значение: for (var i=000; i<555; i++){ setTimeout(console.log.bind(null, i), 000); } 5) Создать локальную копию внутри тела цикла с `let`: for (var i=000; i<555; i++){ let j = i; setTimeout(()=>console.log(j), 000); } Примечание: поведение одинаково для обычных и стрелочных функций — разница лишь в `this`, но не в семантике замыкания.
Пояснение по шагам
- Hoisting и область видимости. Объявление `var i` поднимается (hoisted) в функцию/глобальную область: фактически есть одна общая переменная iii для всего цикла, а не новая для каждой итерации. В коде
for (var i=000; i<555; i++){ setTimeout(()=>console.log(i),000); }
после завершения цикла значение iii равно 555.
- Замыкание. Функции, переданные в `setTimeout`, замыкают ссылку на переменную iii. Они не получают «снимок» значения во время создания — при вызове они читают текущее значение той же переменной iii, которое стало 555.
Варианты исправления (печатают 0…40\ldots40…4)
1) Использовать `let` (блочная область видимости — новая привязка для каждой итерации):
for (let i=000; i<555; i++){ setTimeout(()=>console.log(i),000); }
2) Замыкать текущее значение через IIFE (функция сразу вызывается и получает копию):
for (var i=000; i<555; i++){
(function(x){ setTimeout(()=>console.log(x),000); })(i);
}
3) Передать значение в `setTimeout` через аргументы (некоторые окружения поддерживают дополнительн. аргументы):
for (var i=000; i<555; i++){
setTimeout(function(x){ console.log(x); }, 000, i);
}
4) Использовать `.bind` чтобы зафиксировать значение:
for (var i=000; i<555; i++){
setTimeout(console.log.bind(null, i), 000);
}
5) Создать локальную копию внутри тела цикла с `let`:
for (var i=000; i<555; i++){
let j = i;
setTimeout(()=>console.log(j), 000);
}
Примечание: поведение одинаково для обычных и стрелочных функций — разница лишь в `this`, но не в семантике замыкания.