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