как интерпретировать результат (понимать let и var в js) - PullRequest
0 голосов
/ 18 мая 2019

рассмотрите следующий код

var fs = [];

for(var i=0;i<10;i++){
	fs.push(i => console.log(i));
}

fs.forEach( f => f());

Если функция изменена на:

for(let i=0;i<10;i++){
    fs.push(function(){
        console.log(i)
    });
}

Будет напечатано ожидаемое значение 1,2,3,4,5,6,7,8,9.

Я не понимаю, почему. Может ли кто-нибудь помочь.

Ответы [ 3 ]

4 голосов
/ 18 мая 2019

let и var не имеют значения для вашего кода относительно того, почему вы получаете 10x undefined.

Что имеет значение, так это то, что ваша функция стрелки определена с использованием параметра i (перезаписывает внешний индекс i), тогда как в вашем втором примере это не так.

const fs = [];

for (var i = 0; i < 10; i++) {
  fs.push(() => console.log(i));
}

fs.forEach(f => f());

Если вы добавите этот параметр во второй пример, вы также получите 10x undefined.

const fs = [];

for (let i = 0; i < 10; i++) {
  fs.push(function(i) {
    console.log(i)
  });
}

fs.forEach(f => f());

Разница между var и let в вашем коде более тонкая:

const fs = [];

for (var i = 0; i < 10; i++) {
  fs.push(() => console.log(i));
}

fs.forEach(f => f());

const fs = [];

for (let i = 0; i < 10; i++) {
  fs.push(() => console.log(i));
}

fs.forEach(f => f());

С let каждая функция в массиве имеет локально закрытое закрытие i, тогда как с var во время выполнения i равно 10 (потому что есть только одна переменная i, которая закрыта в каждой функции).

2 голосов
/ 18 мая 2019

Есть 2 разных вопроса, которые необходимо рассмотреть более подробно.Давайте сосредоточимся на первом случае, когда основной проблемой является то, что вы определяете функцию стрелки ES6 и вызываете ее позже без параметров:

i => console.log(i) при преобразовании в анонимную функцию ES5:

function(i){ console.log(i) }

Таким образом, в i => console.log(i) имеется короткое определение ES6 для анонимной функции (также известной как обозначение функции стрелки ES6 ), которая принимает параметр i.

Конечным результатом является то, что console.log(i) пытается напечатать i, то есть undefined, поскольку он не передается этой функции стрелки во время выполнения.

Вы снова нажимаете определение функции, которую затем выполняете без передачи в нее параметра, который необходим для его вывода на консоль.

var fs = [];

for(var i=0; i<10; i++){
  // You are pushing to the array the below anonymous function definition
  fs.push(  
    // You are creating an anonymous function which accepts i and console.logs it
    i => console.log(i)
  );  
}

fs.forEach(
  // You are calling the "pushed" above function definition with NO parameter i
  f => f()
);

Теперь давайте рассмотрим, почему и как работает 2-й пример кода и как var/let играет большую роль в выводе консоли:

let fs = []

// You define i in the for-loop block score
for(var i=0; i<10; i++){
   fs.push(
     // You push an annonymous function definition wich has a reference of i in the same block scope
     // However i is already here 10 since the loop is already done and it takes the last value of i
     function(){ console.log(i) }
   );
}
    
fs.forEach(
   // You call f which has a reference inside to the i from the for loop
   f => f()
);

Так что в этом случае i при использовании var i i, поскольку он не сохраняет свою область видимости лексического блока, обновляется до 10 до того, какconsole.log вызывается.

Давайте попробуем это сейчас с let:

let fs = []

// You define i via LET in the for-loop block score
for(let i=0; i<10; i++){
  fs.push(
    // You push an annonymous function definition wich has a reference of i in the same block scope
    // i retains its lexical scope in for loops
    function(){ console.log(i) }
   );
}

fs.forEach(
  // You call f which has a reference inside to the i from the for loop
  f => f()
);

Немного больше о let:

let позволяет объявлять переменные, которые ограничены в области действия block , выражение или выражение , в котором оно используется.Это в отличие от ключевое слово var , которое определяет переменную глобально или локально для всей функции независимо от области видимости блока.

1 голос
/ 18 мая 2019

В вашем первом примере i in fs.push(i => console.log(i)); является параметром функции стрелки.это фактически эквивалентно fs.push(function(i){ return console.log(i); });

Этот параметр имеет то же имя, что и итератор цикла.В области действия функции, которую вы нажимаете, параметр i имеет приоритет над внешней переменной.Если вы хотите захватить итератор, вы не должны называть его так же, как параметр, например:

fs.push(() => console.log(i));

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

...