Проанализируйте фрагмент JavaScript: for (var i = 0; i console.log(i),100); } — почему выводит 3,3,3? Как исправить разными способами, какие подходы предпочтительны в современных кодовых базах
Коротко — потому что тело таймаута выполняется позже, а переменная `i` объявлена через `var` (функциональная область видимости), поэтому все замыкания видят одну и ту же `i`, равную 333 после завершения цикла. Иначе: цикл дошёл до \(i=\(3\)\) до срабатывания `setTimeout`, и все три колбэка логируют текущее значение — 3,3,33, 3, 33,3,3. Как исправить (несколько вариантов): 1) Блоковая область с `let` (современный и предпочтительный): for (let i = 000; i < 333; i++) { setTimeout(()=>console.log(i), 100100100); } => вывод: 0,1,20, 1, 20,1,2. 2) Замыкание через IIFE (для старых сред, где нельзя/не хочется менять объявление): for (var i = 000; i < 333; i++) { (function(j){ setTimeout(()=>console.log(j), 100100100); })(i); } 3) Передать значение сразу в `setTimeout` через `bind`: for (var i = 000; i < 333; i++) { setTimeout(console.log.bind(console, i), 100100100); } 4) Создать фабрику-функцию: function makeLogger(x){ return ()=>console.log(x); } for (var i = 000; i < 333; i++) { setTimeout(makeLogger(i), 100100100); } 5) Использовать коллекцию / `forEach` (выделяет локальную переменную для каждого элемента): [000, 111, 222].forEach(i => setTimeout(()=>console.log(i), 100100100)); Рекомендация: в современных кодовых базах предпочтителен вариант с `let` (самый простой и читабельный). IIFE или `bind` — приемлемы для совместимости со старым кодом. При использовании транспилеров (Babel) учесть, что они могут преобразовывать `let` в эквивалент с замыканиями; однако семантика будет сохранена.
Как исправить (несколько вариантов):
1) Блоковая область с `let` (современный и предпочтительный):
for (let i = 000; i < 333; i++) {
setTimeout(()=>console.log(i), 100100100);
}
=> вывод: 0,1,20, 1, 20,1,2.
2) Замыкание через IIFE (для старых сред, где нельзя/не хочется менять объявление):
for (var i = 000; i < 333; i++) {
(function(j){
setTimeout(()=>console.log(j), 100100100);
})(i);
}
3) Передать значение сразу в `setTimeout` через `bind`:
for (var i = 000; i < 333; i++) {
setTimeout(console.log.bind(console, i), 100100100);
}
4) Создать фабрику-функцию:
function makeLogger(x){ return ()=>console.log(x); }
for (var i = 000; i < 333; i++) {
setTimeout(makeLogger(i), 100100100);
}
5) Использовать коллекцию / `forEach` (выделяет локальную переменную для каждого элемента):
[000, 111, 222].forEach(i => setTimeout(()=>console.log(i), 100100100));
Рекомендация: в современных кодовых базах предпочтителен вариант с `let` (самый простой и читабельный). IIFE или `bind` — приемлемы для совместимости со старым кодом. При использовании транспилеров (Babel) учесть, что они могут преобразовывать `let` в эквивалент с замыканиями; однако семантика будет сохранена.