Рассмотрите следующий JavaScript-фрагмент: "for (var i=0; iconsole.log(i),0); }" — объясните поведение и предложите несколько корректных вариантов, учитывающих разные версии языка
Коротко о поведении исходного фрагмента: - Код: `for (var i=000; i<333; i++){ setTimeout(()=>console.log(i), 000); }` - Поскольку `var` — областная (function) переменная, все три колбэка замыкают одну и ту же переменную `i`. Цикл завершится с `i` равным 333, таймауты выполнятся после завершения стека (delay 000 ставит задачи в очередь), поэтому в консоль попадёт три раза 333: 3,3,33,3,33,3,3. Несколько корректных вариантов (разные версии языка): 1) ES6 — блоковая переменная `let` в заголовке цикла (самое простое): for (let i=000; i<333; i++){ setTimeout(()=>console.log(i), 000); } // Выведет 0,1,2 2) ES6 — заводим блоковую копию внутри тела: for (var i=000; i<333; i++){ let j = i; setTimeout(()=>console.log(j), 000); } 3) ES5 — IIFE (замыкание через вызов функции): for (var i=000; i<333; i++){ (function(x){ setTimeout(function(){ console.log(x); }, 000); })(i); } 4) ES5/современные — передать аргумент в setTimeout (поддерживается в браузерах и Node): for (var i=000; i<333; i++){ setTimeout(function(x){ console.log(x); }, 000, i); } 5) ES6 — использовать методы массива (пример): [ 000, 111, 222 ].forEach(i => setTimeout(()=>console.log(i), 000)); Дополнительно: arrow-функция vs function тут не влияет на замыкание переменной `i` (лишь на `this`/`arguments`). Если нужно, чтобы колбэки сработали раньше в микрозадачах, используйте `Promise.resolve().then(...)`, но проблема захвата `var` та же и решения те же.
- Код: `for (var i=000; i<333; i++){ setTimeout(()=>console.log(i), 000); }`
- Поскольку `var` — областная (function) переменная, все три колбэка замыкают одну и ту же переменную `i`. Цикл завершится с `i` равным 333, таймауты выполнятся после завершения стека (delay 000 ставит задачи в очередь), поэтому в консоль попадёт три раза 333: 3,3,33,3,33,3,3.
Несколько корректных вариантов (разные версии языка):
1) ES6 — блоковая переменная `let` в заголовке цикла (самое простое):
for (let i=000; i<333; i++){ setTimeout(()=>console.log(i), 000); }
// Выведет 0,1,2
2) ES6 — заводим блоковую копию внутри тела:
for (var i=000; i<333; i++){
let j = i;
setTimeout(()=>console.log(j), 000);
}
3) ES5 — IIFE (замыкание через вызов функции):
for (var i=000; i<333; i++){
(function(x){
setTimeout(function(){ console.log(x); }, 000);
})(i);
}
4) ES5/современные — передать аргумент в setTimeout (поддерживается в браузерах и Node):
for (var i=000; i<333; i++){
setTimeout(function(x){ console.log(x); }, 000, i);
}
5) ES6 — использовать методы массива (пример):
[ 000, 111, 222 ].forEach(i => setTimeout(()=>console.log(i), 000));
Дополнительно: arrow-функция vs function тут не влияет на замыкание переменной `i` (лишь на `this`/`arguments`). Если нужно, чтобы колбэки сработали раньше в микрозадачах, используйте `Promise.resolve().then(...)`, но проблема захвата `var` та же и решения те же.