Проблема тут не в функции или в стрелках, а в области видимости переменной i. В вашем коде i объявлена через var — она имеет функциональную область видимости, поэтому все три замыкания ссылаются на одну и ту же переменную i. После завершения цикла i равно 3, и все функции возвращают это значение. Поэтому вывод будет:
3 3 3
Как это починить — варианты.
1) Самый простой (ES6+): использовать let в цикле — для каждой итерации создаётся своё блочное связывание i:
function makeArr(){ var arr = []; for (let i = 0; i < 3; i++) { arr.push(function(){ return i; }); } return arr; } const a = makeArr(); console.log(a[0](), a[1](), a[2()]); // 0 1 2
(То же с стрелочной функцией: arr.push(() => i); — стрелок ничего не меняет в плане захвата переменных, он просто лексически связывает this.)
2) Если нужно поддерживать старый JS (без let), создать новую переменную для каждой итерации через IIFE:
function makeArr(){ var arr = []; for (var i = 0; i < 3; i++) { (function(j){ arr.push(function(){ return j; }); })(i); } return arr; }
3) Или фабрика-функция / стрелка с немедленным вызовом:
function makeArr(){ var arr = []; for (var i = 0; i < 3; i++) { arr.push(((j) => () => j)(i)); // либо: arr.push((function(j){ return function(){return j} })(i)); } return arr; }
4) Ещё вариант — внутри тела цикла создать const j = i и замыкаться на j:
for (var i = 0; i < 3; i++) { const j = i; arr.push(() => j); }
Комментарии по стрелочным функциям и const:
Заменив обычную функцию на стрелочную (function() → () => ), вы не уберёте проблему саму по себе: стрелочная функция тоже захватывает переменные лексически, поэтому при var i все стрелки вернут 3. Стрелки отличаются в первую очередь поведением this, а не семантикой замыканий переменных.const a = makeArr(); никак не влияет на содержимое массива — const запрещает переназначение переменной a, но не делает элементы массива неизменяемыми.Нельзя писать for (const i = 0; ...; i++) — const нельзя инкрементировать. Для цикла используйте let.
Вывод: проблема — общая переменная i (объявлена через var). Решение — дать каждой итерации своё связывание (let в заголовке цикла, новая переменная в теле цикла или IIFE / фабрика).
Проблема тут не в функции или в стрелках, а в области видимости переменной i. В вашем коде i объявлена через var — она имеет функциональную область видимости, поэтому все три замыкания ссылаются на одну и ту же переменную i. После завершения цикла i равно 3, и все функции возвращают это значение. Поэтому вывод будет:
3 3 3
Как это починить — варианты.
1) Самый простой (ES6+): использовать let в цикле — для каждой итерации создаётся своё блочное связывание i:
function makeArr(){
var arr = [];
for (let i = 0; i < 3; i++) {
arr.push(function(){ return i; });
}
return arr;
}
const a = makeArr();
console.log(a[0](), a[1](), a[2()]); // 0 1 2
(То же с стрелочной функцией: arr.push(() => i); — стрелок ничего не меняет в плане захвата переменных, он просто лексически связывает this.)
2) Если нужно поддерживать старый JS (без let), создать новую переменную для каждой итерации через IIFE:
function makeArr(){
var arr = [];
for (var i = 0; i < 3; i++) {
(function(j){
arr.push(function(){ return j; });
})(i);
}
return arr;
}
3) Или фабрика-функция / стрелка с немедленным вызовом:
function makeArr(){
var arr = [];
for (var i = 0; i < 3; i++) {
arr.push(((j) => () => j)(i)); // либо: arr.push((function(j){ return function(){return j} })(i));
}
return arr;
}
4) Ещё вариант — внутри тела цикла создать const j = i и замыкаться на j:
for (var i = 0; i < 3; i++) {
const j = i;
arr.push(() => j);
}
Комментарии по стрелочным функциям и const:
Заменив обычную функцию на стрелочную (function() → () => ), вы не уберёте проблему саму по себе: стрелочная функция тоже захватывает переменные лексически, поэтому при var i все стрелки вернут 3. Стрелки отличаются в первую очередь поведением this, а не семантикой замыканий переменных.const a = makeArr(); никак не влияет на содержимое массива — const запрещает переназначение переменной a, но не делает элементы массива неизменяемыми.Нельзя писать for (const i = 0; ...; i++) — const нельзя инкрементировать. Для цикла используйте let.Вывод: проблема — общая переменная i (объявлена через var). Решение — дать каждой итерации своё связывание (let в заголовке цикла, новая переменная в теле цикла или IIFE / фабрика).