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