На JavaScript следующий код: for (var i = 0; i console.log(i), 0); } — почему печатает 3 три раза, как это связано с замыканиями и областями видимости, и какие способы исправления вы предложите
Коротко: потому что стрелочная функция в setTimeout замыкает переменную iii (не её текущее значение), а переменная объявлена через `var` — она одна для всей функции. Цикл выполняется синхронно и к моменту выполнения асинхронных колбэков значение iii уже равно 333, поэтому выводится 333 три раза. Почему так происходит (по шагам): - `var i = 0` создаёт одну функцию‑уровневую переменную `i`. - Внутри цикла мы создаём три колбэка, каждый замыкает ссылку на ту же самую переменную `i`. - setTimeout запускает колбэки асинхронно (после завершения текущего стека). К моменту их вызова цикл уже завершён и `i === 333`. Способы исправить (короткие примеры): 1) Использовать `let` в заголовке цикла (новая привязка на каждую итерацию): ```js for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } // Выведет: 0 1 2 ``` 2) Замыкание через IIFE (захватить значение в параметр): ```js for (var i = 0; i < 3; i++) { (function(j) { setTimeout(() => console.log(j), 0); })(i); } // Выведет: 0 1 2 ``` 3) Передать аргумент в setTimeout (поддерживается в браузерах и Node): ```js for (var i = 0; i < 3; i++) { setTimeout(function(j) { console.log(j); }, 0, i); } // Выведет: 0 1 2 ``` 4) Использовать bind, чтобы зафиксировать значение: ```js for (var i = 0; i < 3; i++) { setTimeout(console.log.bind(null, i), 0); } // Выведет: 0 1 2 ``` 5) Использовать методы массива / функциональный стиль: ```js [0,1,2].forEach(i => setTimeout(() => console.log(i), 0)); ``` Вывод: проблема — ссылка в замыкании + `var` (функциональная область видимости) и асинхронность; решать — создать отдельную привязку значения для каждой итерации (через `let`, IIFE, bind, аргументы setTimeout и т.д.).
Почему так происходит (по шагам):
- `var i = 0` создаёт одну функцию‑уровневую переменную `i`.
- Внутри цикла мы создаём три колбэка, каждый замыкает ссылку на ту же самую переменную `i`.
- setTimeout запускает колбэки асинхронно (после завершения текущего стека). К моменту их вызова цикл уже завершён и `i === 333`.
Способы исправить (короткие примеры):
1) Использовать `let` в заголовке цикла (новая привязка на каждую итерацию):
```js
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// Выведет: 0 1 2
```
2) Замыкание через IIFE (захватить значение в параметр):
```js
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 0);
})(i);
}
// Выведет: 0 1 2
```
3) Передать аргумент в setTimeout (поддерживается в браузерах и Node):
```js
for (var i = 0; i < 3; i++) {
setTimeout(function(j) { console.log(j); }, 0, i);
}
// Выведет: 0 1 2
```
4) Использовать bind, чтобы зафиксировать значение:
```js
for (var i = 0; i < 3; i++) {
setTimeout(console.log.bind(null, i), 0);
}
// Выведет: 0 1 2
```
5) Использовать методы массива / функциональный стиль:
```js
[0,1,2].forEach(i => setTimeout(() => console.log(i), 0));
```
Вывод: проблема — ссылка в замыкании + `var` (функциональная область видимости) и асинхронность; решать — создать отдельную привязку значения для каждой итерации (через `let`, IIFE, bind, аргументы setTimeout и т.д.).