Давайте сначала обсудим, чем объявлены переменные withvar и let.

И let, и var используются для объявления переменных, которые могут быть дополнительно инициализированы во время объявления.

Согласно MDN:

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

Оператор let объявляет локальную переменную с блочной областью видимости, при необходимости инициализируя ее значением.

Но есть существенные различия с точки зрения их создания и использования.

Отличия на момент создания и исполнения

Анализ программы JavaScript - это двухэтапный процесс.

Двухэтапный процесс для переменных с var

  1. Этап создания. На этом этапе выполняется поиск всех объявлений переменных и функций в файле JavaScript и создается память для них в глобальном или функциональном контексте выполнения. например
1| console.log(a); // undefined
2| var a = 10;

Как я упоминал выше, при синтаксическом анализе вышеуказанной программы этап создания будет искать все объявления переменных и игнорировать все другие операторы. Поэтому будет создана память для переменной a, которая по умолчанию будет присвоена undefined.

2. Этап выполнения - на этом этапе выполняется построчная оценка каждого оператора и выполняется программа.

В приведенном выше примере строка 1 попытается найти переменную a в глобальном контексте выполнения и попытается распечатать ее значение, которое в то время не определено.

Это поведение также известно как подъем, при котором переменная может использоваться даже до ее объявления.

Двухэтапный процесс для переменных с let

  1. Этап создания - аналогично этапам создания для var, память создается и для let операторов, но в этом случае память не назначена для undefined по умолчанию. Вместо этого переменная попадает в временную мертвую зону (TDZ), что означает, что переменную нельзя использовать до ее объявления.
1| console.log(a); // Refernce Error
2| let a = 10;

2. Этап выполнения - В строке номер 1 код теперь выдает эталонную ошибку, то есть переменную a нельзя использовать до ее объявления (из-за TDZ ).

Различия в объемах переменных

Давайте разберемся на примерах, чем var и let переменные различаются по своему объему.

Глобальное загрязнение объекта

var a = 10;
console.log(a); // 10
console.log(window.a); // a

Это означает, что когда переменная создается в глобальном контексте (с помощью var), она загрязняет глобальный объект окна.

let a = 10;
console.log(a); // 10
console.log(window.a); // undefined

Но переменные, созданные с помощью let в глобальном контексте, не загрязняют глобальный объект окна.

Повторное объявление переменных

Мы можем повторно объявить var переменных в том же контексте выполнения, но не можем сделать это с переменными let.

var a = 10;
var a = 20; // works fine
let b = 10;
let b = 20; // Error

Объем функции по сравнению с Область действия блока

function a() {
  var b = 10;
  if (b > 0) {
    var b = 20; // same variables
  }
  console.log(x); // 20
}
function z() {
  var x = 10;
  if (x > 0) {
    let x = 20; // different variables
  }
  console.log(x); // 10
}

Замыкания 2.0

Давайте сначала поймем замыкание. По MDN

Замыкание - это сочетание функции, объединенной (заключенной), со ссылками на ее окружающее состояние (лексическое окружение). Другими словами, замыкание дает вам доступ к области внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз, когда создается функция, во время создания функции.

Замыкания полезны, потому что они позволяют вам связывать данные (лексическую среду) с функцией, которая работает с этими данными. В этом есть очевидные параллели с объектно-ориентированным программированием, где объекты позволяют связывать данные (свойства объекта) с одним или несколькими методами.

function outer() {
  var a = 10;
  return function inner() {
    return a + 10;
  }
}
var inner = outer();
console.log(inner()); // 20

Замыкания также очень полезны при реализации модульного JavaScript. В этом отношении может оказаться полезным IIFE (Выражение немедленного вызова функции).

var App = {};
(function(){
  var loggedInUser = {
    name: "John Doe",
    email: "[email protected]",
    password: "somepassword",
    token: "sometoken",
  }
  function getLoggedInUser() {
    var user = { ...loggedInUser };
    delete user.password;
    return user;
  }
  function updateToken(token) {
    loggedInUser = {...loggedInUser, token};
  }
App.auth = {
    getLoggedInUser,
    updateToken,
  }
})()
console.log(App.auth.getLoggedInUser()); // Works Fine
console.log(getLoggedInUser()); // Reference Error

Но когда я впервые посмотрел на это, я задумался, почему мы должны использовать одну дополнительную анонимную функцию и немедленно ее вызывать. Но поскольку до ES2015 в JavaScript не было классов и пространств имен, это был единственный шаблон для создания модулей (создание частного состояния и реализаций и предоставление только общедоступных API, которые будут использоваться в других модулях).

Теперь, почему IIFE, поскольку для создания состояния или частных свойств / переменных у нас было var (до ES2015), а var - это переменная функциональной области, у нас не было другого способа, кроме как обернуть это состояние и методы во внешнюю функцию. А поскольку мы хотим запустить ее один раз, внешняя функция должна быть анонимной и вызываться после ее объявления.

Теперь попробуем реализовать тот же модуль, используя только блоки.

let App = {};
{
  let loggedInUser = {
    name: "John Doe",
    email: "[email protected]",
    password: "somepassword",
    token: "sometoken",
  }
  let getLoggedInUser = function() {
    var user = { ...loggedInUser };
    delete user.password;
    return user;
  }
  let updateToken = function(token) {
    loggedInUser = {...loggedInUser, token};
  }
  App.auth = {
    getLoggedInUser,
    updateToken,
  }
}
console.log(App.auth.getLoggedInUser()); // Works Fine
console.log(getLoggedInUser()); // Reference Error

Большой!! Теперь мы избавились от анонимной функции-оболочки и вызова функции. Поскольку let - это переменная с областью видимости блока, нам просто нужно обернуть все частные переменные и методы внутри блока.

Знаменитый вопрос для интервью

Мне лично задали этот вопрос, в котором есть var-forloop, а внутри блока цикла for есть setTimeout, который печатает переменную i. Давайте проверим это ниже.

for(var i=0; i<3; i++) {
  setTimeout(() => console.log(i), 1000);
}
> 3 not 0
> 3 not 1
> 3 not 2

Если вы попытаетесь запустить приведенный выше фрагмент кода, вы получите 3 раза по 3, а не 0, 1 и 2, как вы могли ожидать.

Давайте узнаем, как var работает здесь.

  • Поскольку var i не является переменной с областью видимости блока, i будет создан в глобальной области, если вы напечатаете window.i, вы получите 3 (что ожидается).
  • Поскольку внутренняя функция, которая передается как обратный вызов в setTimeout, в каждом цикле регистрируется новый обратный вызов, который лексически привязан к переменной i, которая была объявлена ​​в глобальном контексте.
  • Поскольку все три обратных вызова лексически привязаны к одному и тому же window.i, то есть 3, когда вызываются обратные вызовы. Каждый раз печатается 3.

Как теперь решить эту проблему?

for(let i=0; i<3; i++) {
  setTimeout(() => console.log(i), 1000);
}
// Output 
> 0
> 1
> 2

Преобразуйте var i в let i, и вы получите 0, 1 и 2, как и ожидалось. Но как изменение просто var на let отлично работает в этом случае.

Давайте узнаем, как let работает здесь.

  • В каждом цикле создается новый блок, и для каждого блока создается let i.
  • Теперь внутри этого блока обратный вызов ликсификационно привязан к let i, который будет равен 0, 1 и 2.
  • Поэтому выполняются обратные вызовы, мы получаем 0, 1 и 2.

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

for(let i=0; i<3; i++) {
  (function(a){
    setTimeout(() => console.log(a), 1000);
  })(i)
}

Надеюсь, вам понравилось читать. Спасибо!!!