Петли и затворы. Для и Var

Я нашел много тем, объясняющих эту проблему, о том, как я могу исправить следующий код с помощью var, как этот http://conceptf1.blogspot.com/2013/11/javascript-closures.html или этот Закрытие JavaScript внутри циклов - простой практический пример.

Но я действительно не могу понять, почему он не работает при использовании var и работает при использовании let.

var funcs = [];        
for (var i = 0; i < 3; i++) {        //  let create 3 functions
  funcs[i] = function() {            //  and store them in funcs
    console.log("My value: " + i);   //  each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                        //  and now let run each one to see
}
// outputs 3 3 3

У меня действительно нет подсказки...

Ответ 1

ES6 let - это область блока, которая означает, что у нее есть собственная область внутри {}, как и многие другие традиционные языки. Но в отличие от var есть глобальная переменная в вашем коде.

В первом цикле for function присваивается func[i] 3 раза с конечным значением 3, но не выполняется. Если вы выполните функцию внутри первого loop, вы получите ожидаемый результат, как будто:

var funcs = [];
for (var i = 0; i < 3; i++) {      // let create 3 functions
  funcs[i] = function() {          // and store them in funcs
    console.log("My value: " + i); // each should log its value.
  };
  funcs[i](); // execution of func
}

Ответ 2

Поскольку var является областью действия функции (т.е. имеет объем окружения), тогда как let и const являются блочными областями - таким образом, они имеют свои собственные значения внутри каждого блока (будь то if - или else -блока или каждой итерации цикла, как в вашем случае). Переменные с блочной областью не определены вне блока. Переменные с функциональной областью остаются до конца функции.

Ваша переменная i имеет функцию-область действия, что означает, что после завершения цикла все еще присутствует вне первого цикла и имеет значение 3. Поэтому, как только вы вызываете функцию массива, она берет i из верхней области (которая 3) и выводит ее. Если вы использовали let, например, вы получили бы новое связывание для каждой итерации, и ваше значение i сохранит начальное значение объявления.

Изменить: Поэтому для вывода последовательных значений вам нужно заменить var на let:

for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    // now `i` is bound to the scope & keeps its initial value
    console.log("My value: " + i); 
  };
}

Ответ 3

В отличие от let, var выведен за пределы области цикла. Фактически, ваша переменная i всегда будет равна последней итерации (которая в вашем примере равна 3). let не имеет этой проблемы, потому что он не поднят.