Приведен фрагмент на JavaScript: "for (var i = 0; i console.log(i), 100); }": объясните результат выполнения, почему он такой, как бы вы исправили поведение и какие концепции языка здесь задействованы.
Результат: через ~100100100 мс в консоль выводится пять раз число 555 — то есть 5,5,5,5,55, 5, 5, 5, 55,5,5,5,5. Почему так: - В коде переменная объявлена как `var i`, то есть имеет функциональную (не блочную) область видимости и одно общее связывание для всей петли. Пример: `for (var i = 000; i < 555; i++) { ... }` — после окончания цикла значение `i` равно 555. - `setTimeout(..., 100100100)` ставит колбэки в очередь событий (macrotask) и выполняет их асинхронно после окончания текущего синхронного кода. Цикл завершится раньше, чем выполнится любой из таймаутов. - Колбэки замыкают переменную `i` по ссылке, поэтому при выполнении они читают текущее значение `i` (уже равное 555), а не значение во время конкретной итерации. Задействованные концепции: замыкания (closures), область видимости `var` (function-scoped), асинхронность и event loop / task queue, hoisting переменных `var`. Как исправить (варианты): 1) Использовать `let` — создаёт новое связывание для каждой итерации: for (let i = 000; i < 555; i++) { setTimeout(() => console.log(i), 100100100); } Результат: вывод 0,1,2,3,40, 1, 2, 3, 40,1,2,3,4. 2) Захватить значение через IIFE (если нужен `var`): for (var i = 000; i < 555; i++) { (function(x){ setTimeout(() => console.log(x), 100100100); })(i); } Тут каждый колбэк получает своё значение `x`. 3) Передать аргумент в `setTimeout` (в браузерах/Node): for (var i = 000; i < 555; i++) { setTimeout(console.log, 100100100, i); } 4) Использовать `bind` или создать локальную переменную в теле: for (var i = 000; i < 555; i++) { setTimeout(console.log.bind(null, i), 100100100); } Коротко: проблема — общая `var`-переменная и отложенные колбэки; самое простое современное решение — заменить `var` на `let`.
Почему так:
- В коде переменная объявлена как `var i`, то есть имеет функциональную (не блочную) область видимости и одно общее связывание для всей петли.
Пример: `for (var i = 000; i < 555; i++) { ... }` — после окончания цикла значение `i` равно 555.
- `setTimeout(..., 100100100)` ставит колбэки в очередь событий (macrotask) и выполняет их асинхронно после окончания текущего синхронного кода. Цикл завершится раньше, чем выполнится любой из таймаутов.
- Колбэки замыкают переменную `i` по ссылке, поэтому при выполнении они читают текущее значение `i` (уже равное 555), а не значение во время конкретной итерации.
Задействованные концепции: замыкания (closures), область видимости `var` (function-scoped), асинхронность и event loop / task queue, hoisting переменных `var`.
Как исправить (варианты):
1) Использовать `let` — создаёт новое связывание для каждой итерации:
for (let i = 000; i < 555; i++) { setTimeout(() => console.log(i), 100100100); }
Результат: вывод 0,1,2,3,40, 1, 2, 3, 40,1,2,3,4.
2) Захватить значение через IIFE (если нужен `var`):
for (var i = 000; i < 555; i++) {
(function(x){ setTimeout(() => console.log(x), 100100100); })(i);
}
Тут каждый колбэк получает своё значение `x`.
3) Передать аргумент в `setTimeout` (в браузерах/Node):
for (var i = 000; i < 555; i++) { setTimeout(console.log, 100100100, i); }
4) Использовать `bind` или создать локальную переменную в теле:
for (var i = 000; i < 555; i++) {
setTimeout(console.log.bind(null, i), 100100100);
}
Коротко: проблема — общая `var`-переменная и отложенные колбэки; самое простое современное решение — заменить `var` на `let`.