Compare Scopes of the var and let Keywords

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

Ключевое слово let ведет себя аналогично, но с некоторыми дополнениями. Когда вы объявляете переменную с ключевым словом let внутри блока, оператора или выражения, его область действия ограничена этим блоком, оператором или выражением. Например:

var numArray = [];
for (var i = 0; i < 3; i++) { 
  numArray.push(i); 
} 
console.log(numArray); // [0, 1, 2]
console.log(i); // 3

С ключевым словом var переменная i объявляется глобально. Поэтому, когда i++ выполняется, он обновляет глобальную переменную.

На самом деле можно чуть-чуть переписать этот код и получить следующее:

// Объявляем массив
var numArray = [];
// Объявляем глобальную переменную i, пока еще без значения
var i;
// Пишем цикл for, задав переменной i начальное значение 0
for (i = 0; i < 3; i++) { 
  numArray.push(i); 
} 
console.log(numArray); // [0, 1, 2]
console.log(i); // 3 

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

var printNumTwo;
for (var i = 0; i < 3; i++) { 
  if (i===2) { 
    /*
      Если i равно 2, переменной printNumTwo 
      задаем в качестве значения новую фукнцию, 
      которая при выполнении должна вернуть 
      значение переменной i
    */
    printNumTwo=function() { 

      /*
        Здесь предполагается, что будет возвращено 2,
        ведь i равнялась 2, 
        когда мы создавали функцию
      */
      return i; 
    }; 
  } 
} 
console.log(printNumTwo()); 

Как вы можете видеть, printNumTwo() печатает 3, а не 2 (как изначально предполагалось). Это связано с тем, что значение, присвоенное переменной i было обновлено далее в цикле, и printNumTwo() возвращает актуальное значение глобальной переменной i, а не значение, которое i имела при создании функции в цикле for.

Ключевое слово let ведет себя по-другому:

let printNumTwo;
for (let i = 0; i < 3; i++) { 
  if (i===2) { 
    printNumTwo=function() { 
      return i; 
    }; 
  } 
} 
console.log(printNumTwo()); // 2
console.log(i); // Uncaught ReferenceError: i is not defined 

Здесь в вызове console.log(i) мы получаем ошибку, что переменная i не объявлена. Это правильно, так как здесь она объявляется только в инструкции цикла for и не видна за его пределами. printNumTwo() вернула правильное значение, потому что при каждой итерации цикла for создавалась новая переменная i с ключевым словом let и с уникальным значением (0, 1 и 2 соответственно в каждой итерации). Такое поведение называется Замыканием.

Исправьте код, чтобы в выражении if создавалась отдельная переменная i, которая не будет пересекаться с переменной i, объявленной в первой строке функции.

Ключевое слово var не должно использоваться.

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