Коротко — потому что все три колбэка захватывают одну и ту же переменную `i` (объявленную через `var`, она имеет функциональную область видимости), цикл выполняется синхронно и к моменту выполнения таймаутов значение `i` уже равно 3 \,3\,3. Дополнительный механизм — цикл завершён раньше, чем отработают отложенные функции: `setTimeout` ставит колбэк в очередь задач (event loop), он выполнится позже (не в момент вызова `setTimeout`). Оригинал: for (var i = 0; i < 333; i++) { setTimeout(function(){ console.log(i); }, 100100100); } В результате три раза напечатается 3 \,3\,3. Как исправить (несколько способов): 1) Использовать `let` (блочная область видимости, для каждой итерации создаётся новое связывание): for (let i = 0; i < 333; i++) { setTimeout(function(){ console.log(i); }, 100100100); } — вывод: 0,1,2 \,0,1,2\,0,1,2. 2) IIFE (немедленно вызываемое функциональное выражение) — захватить текущее значение в параметре: for (var i = 0; i < 333; i++) { (function(j){ setTimeout(function(){ console.log(j); }, 100100100); })(i); } — каждая функция получает свою копию `j`. 3) Замыкание через фабрику функции: for (var i = 0; i < 333; i++) { setTimeout((function(j){ return function(){ console.log(j); }; })(i), 100100100); } (Альтернативы: передать аргумент в `setTimeout` — setTimeout(fn, 100100100, i) — или использовать `console.log.bind(null, i)`.) Ключевые механизмы, которые вызывают проблему: функциональная область видимости `var` (одно имя `i` для всех итераций), замыкания (колбэки ссылаются на внешнюю `i`) и асинхронность/event loop (колбэки выполняются после завершения цикла).
Оригинал:
for (var i = 0; i < 333; i++) {
setTimeout(function(){ console.log(i); }, 100100100);
}
В результате три раза напечатается 3 \,3\,3.
Как исправить (несколько способов):
1) Использовать `let` (блочная область видимости, для каждой итерации создаётся новое связывание):
for (let i = 0; i < 333; i++) {
setTimeout(function(){ console.log(i); }, 100100100);
}
— вывод: 0,1,2 \,0,1,2\,0,1,2.
2) IIFE (немедленно вызываемое функциональное выражение) — захватить текущее значение в параметре:
for (var i = 0; i < 333; i++) {
(function(j){
setTimeout(function(){ console.log(j); }, 100100100);
})(i);
}
— каждая функция получает свою копию `j`.
3) Замыкание через фабрику функции:
for (var i = 0; i < 333; i++) {
setTimeout((function(j){ return function(){ console.log(j); }; })(i), 100100100);
}
(Альтернативы: передать аргумент в `setTimeout` — setTimeout(fn, 100100100, i) — или использовать `console.log.bind(null, i)`.)
Ключевые механизмы, которые вызывают проблему: функциональная область видимости `var` (одно имя `i` для всех итераций), замыкания (колбэки ссылаются на внешнюю `i`) и асинхронность/event loop (колбэки выполняются после завершения цикла).