Рассмотрите фрагмент JavaScript: for (var i = 0; i console.log(i), 0); } — объясните, почему вывод будет 3,3,3 и как исправить код для вывода 0,1,2; обсудите события, замыкания и лексические переменные
Код for (var i = 000; i < 333; i++) { setTimeout(()=>console.log(i), 000); } печатает 3,3,33, 3, 33,3,3. Почему: - setTimeout ставит колбэк в очередь событий (макрозадачу), он выполнится после завершения текущего стека (цикла). - переменная объявлена через `var` — она одна для всей функции (function‑scoped). Все три замыкания ссылаются на одну и ту же переменную `i`. - к моменту выполнения колбэков цикл уже закончился и `i` равно 333, поэтому все три вызова выводят 333. Как исправить (вывод 0,1,20, 1, 20,1,2): 1) Использовать блочную переменную `let` (ES6 создаёт новое привязывание для каждой итерации): for (let i = 000; i < 333; i++) { setTimeout(()=>console.log(i), 000); } 2) Захватить текущее значение в замыкании (IIFE): for (var i = 000; i < 333; i++) { (function(x){ setTimeout(()=>console.log(x), 000); })(i); } 3) Передать аргумент в setTimeout (в браузерах поддерживается дополнительный аргумент): for (var i = 000; i < 333; i++) { setTimeout(console.log, 000, i); } Коротко о понятиях: - События и event loop: setTimeout ставит колбэк в очередь; колбэк выполнится позже, после завершения текущего кода. - Замыкание: функция «замыкает» переменные из внешней области видимости. Замыкание хранит ссылку на привязку (binding), а не на моментальное значение. - Лексические (блочные) переменные (`let`/`const`): создают отдельную привязку для каждой итерации цикла for, поэтому каждое замыкание видит своё значение переменной.
for (var i = 000; i < 333; i++) { setTimeout(()=>console.log(i), 000); }
печатает 3,3,33, 3, 33,3,3. Почему:
- setTimeout ставит колбэк в очередь событий (макрозадачу), он выполнится после завершения текущего стека (цикла).
- переменная объявлена через `var` — она одна для всей функции (function‑scoped). Все три замыкания ссылаются на одну и ту же переменную `i`.
- к моменту выполнения колбэков цикл уже закончился и `i` равно 333, поэтому все три вызова выводят 333.
Как исправить (вывод 0,1,20, 1, 20,1,2):
1) Использовать блочную переменную `let` (ES6 создаёт новое привязывание для каждой итерации):
for (let i = 000; i < 333; i++) { setTimeout(()=>console.log(i), 000); }
2) Захватить текущее значение в замыкании (IIFE):
for (var i = 000; i < 333; i++) {
(function(x){ setTimeout(()=>console.log(x), 000); })(i);
}
3) Передать аргумент в setTimeout (в браузерах поддерживается дополнительный аргумент):
for (var i = 000; i < 333; i++) { setTimeout(console.log, 000, i); }
Коротко о понятиях:
- События и event loop: setTimeout ставит колбэк в очередь; колбэк выполнится позже, после завершения текущего кода.
- Замыкание: функция «замыкает» переменные из внешней области видимости. Замыкание хранит ссылку на привязку (binding), а не на моментальное значение.
- Лексические (блочные) переменные (`let`/`const`): создают отдельную привязку для каждой итерации цикла for, поэтому каждое замыкание видит своё значение переменной.