Найдите и объясните проблему в JavaScript-коде: "for (var i = 0; i console.log(i), 100);" — как исправить, если нужно вывести 0, 1, 2, и какие механизмы языка здесь ответственны за поведение?
Проблема: колбэки замыкают одну переменную `var i` (функциональная область видимости). Цикл `for (var i = 0; i < 3; i++)` успевает закончиться до срабатывания таймаутов, поэтому в момент выполнения `console.log(i)` значение `i` равно `3`. В итоге вывод будет 3,3,33,3,33,3,3, а не 0,1,20,1,20,1,2. Механизмы языка, ответственные за поведение: - var — функциональная (не блочная) область видимости: одна общая переменная `i` для всех итераций. - Замыкания: колбэки получают доступ к той же самой переменной `i`, а не к её копии. - Event loop / макротаски: `setTimeout` выполняет колбэк позже, после завершения цикла. - В ES6 `let` даёт пер-итерационный binding: для каждой итерации создаётся своё блочное привязывание `i`. Как исправить (несколько вариантов): 1) Использовать `let` в заголовке цикла (самый простой современный способ): for (let i = 0; i console.log(i), 100); 2) Замкнуть текущее значение через IIFE: for (var i = 0; i console.log(x), 100); })(i); 3) Передать значение как аргумент таймауту (поддерживается в браузерах и Node): for (var i = 0; i console.log(x), 100, i); 4) Использовать bind для привязки аргумента: for (var i = 0; i < 3; i++) setTimeout(console.log.bind(null, i), 100); Любой из этих способов даст ожидаемый вывод 0,1,20,1,20,1,2.
Механизмы языка, ответственные за поведение:
- var — функциональная (не блочная) область видимости: одна общая переменная `i` для всех итераций.
- Замыкания: колбэки получают доступ к той же самой переменной `i`, а не к её копии.
- Event loop / макротаски: `setTimeout` выполняет колбэк позже, после завершения цикла.
- В ES6 `let` даёт пер-итерационный binding: для каждой итерации создаётся своё блочное привязывание `i`.
Как исправить (несколько вариантов):
1) Использовать `let` в заголовке цикла (самый простой современный способ):
for (let i = 0; i console.log(i), 100);
2) Замкнуть текущее значение через IIFE:
for (var i = 0; i console.log(x), 100); })(i);
3) Передать значение как аргумент таймауту (поддерживается в браузерах и Node):
for (var i = 0; i console.log(x), 100, i);
4) Использовать bind для привязки аргумента:
for (var i = 0; i < 3; i++) setTimeout(console.log.bind(null, i), 100);
Любой из этих способов даст ожидаемый вывод 0,1,20,1,20,1,2.