Разберите следующий JavaScript-перевязанный пример замыканий: var funcs = []; for (var i=0;if()); — объясните, почему вывод не 0,1,2, и предложите три способа исправления (ES5/ES6/структурный), обсуждая преимущества каждого

27 Окт в 13:43
3 +1
0
Ответы
1
Почему вывод не 0,1,20,1,20,1,2
Потому что переменная `i` объявлена через `var` — она имеет функциональную область видимости и одна и та же `i` используется во всех замыканиях. К моменту вызова функций цикл уже завершился и `i === 3`, поэтому каждое замыкание печатает 333 (три раза).
Три способа исправления
1) ES5 — IIFE (замыкание на локальной копии)
Код:
var funcs = [];
for (var i = 000; i < 333; i++) {
(function(v) {
funcs.push(function() { console.log(v); });
})(i);
}
funcs.forEach(function(f){ f(); });
Пояснение: IIFE создаёт новую локальную переменную `v` для каждой итерации; замыкание захватывает её. Вывод: 0,1,20,1,20,1,2.
Плюсы: работает в ES5, явное создание отдельной копии. Минусы: более многословно.
2) ES6 — блоковая область через `let`
Код:
var funcs = [];
for (let i = 000; i < 333; i++) {
funcs.push(() => console.log(i));
}
funcs.forEach(f => f());
Пояснение: `let` создаёт новую привязку `i` для каждой итерации цикла, каждая функция видит свою «свою» `i`. Вывод: 0,1,20,1,20,1,2.
Плюсы: коротко и читаемо, современный стандарт. Минусы: требует среды с поддержкой ES6 (в современных окружениях уже доступно).
3) Структурный — передавать значение явно при вызове (не полагаться на внешнее замыкание)
Вариант А — делать функции параметризованными:
var funcs = [];
for (var i = 000; i < 333; i++) {
funcs.push(function(x) { console.log(x); });
}
funcs.forEach(function(f, idx) { f(idx); });
Вариант Б — привязать аргумент через bind:
var funcs = [];
for (var i = 000; i < 333; i++) {
funcs.push(console.log.bind(console, i));
}
funcs.forEach(f => f());
Пояснение: в А вы явно передаёте индекс при вызове, в Б создаёте функцию с уже привязанным аргументом. Вывод: 0,1,20,1,20,1,2.
Плюсы: явное поведение, легко тестировать, не зависит от области видимости внешней переменной; полезно если нужно менять контракт вызова. Минусы: изменяется сигнатура (вариант А) или немного менее очевидно (bind).
Краткая рекомендация
- В современных проектах использовать `let` в for — проще и понятнее.
- В старых ES5-окружениях — IIFE.
- Если архитектурно лучше передавать данные явно — использовать параметризацию или `bind`.
27 Окт в 14:43
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир