Давайте узнаем, почему в JavaScript существует два способа объявления переменных, концепции подъема и закрытия.

До ECMAScript6 (2015) JavaScript имел только глобальную область действия и область действия функции. Теперь у нас есть еще один: Block Scope. А с ES6 были введены два важных новых ключевых слова JavaScript: let и const.

Что мы подразумеваем под масштабом?

Область действия определяет область, в которой ваша переменная видна или доступна.

Мы можем говорить о двух разных областях: глобальной и локальной. Из них локальный имеет два подтипа: функция и блок.

Чтобы лучше понять, что такое область видимости, рассмотрим следующий пример:

const message = 'Hello';
console.log(message);     // 'Hello'

Хорошо, мы напечатали константу сообщения как «Привет», как и ожидалось. Давайте посмотрим, что произойдет, когда мы переместим объявление в оператор if:

if (true) {
const message = 'Hello';
}
console.log(message); // Uncaught ReferenceError: message is not defined"

Мы не могли печатать, потому что пытались печатать за рамками, которые мы определили. Это недоступно.

Область действия — это правило, которое говорит, что вы не можете получить доступ к переменной, кроме этой области.

Когда переменная имеет глобальную область действия, это означает, что она доступна из любой точки вашей программы. Глобальная область — это самая внешняя область. Он доступен из любой внутренней (также известной как локальной) области.

Давайте погрузимся в локальные области видимости.

Область блокировки:

То, что мы называем блоками, представляет собой фрагменты кода, разделенные фигурными скобками.

{
StatementList
}

При желании блоки можно маркировать.

LabelIdentifier: {
StatementList
}

Мы привели первый пример выше для области блока. Там, где мы пытались объявить константу внутри оператора if, мы столкнулись с переменной блочной области.

Область действия:

Как следует из названия, он упоминается как доступный внутри функции.

function ring() {
// "ring" function scope
var message = 'One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them.';
console.log(message); // 'One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them.'
}

ring();
console.log(message); // Uncaught ReferenceError: message is not defined"

Вот и все! Поскольку это была функция. Мы снова не смогли напечатать переменную сообщения

И еще одна важная вещь, связанная с областями, заключается в том, что внутренняя область может обращаться к переменным своей внешней области.

function ring() {
// "ring" function scope
const message = 'One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them.';
if (true) {
// "if" code block scope
const carrier = 'Frodo';
console.log(message); // 'One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them.'

}
console.log(carrier); // throws ReferenceError
}

ring();

Мы могли напечатать переменную message во внутренней области видимости (оператор if), но не смогли напечатать переменную carrier во внешней области видимости.

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

Различия между let и var:

var — это старый способ объявления переменной и самое слабое ключевое слово. Почему? Потому что:

  • var ограничен функцией
  • let ограничен блоком
  • var можно обновить, а также повторно объявить
  • let можно обновить, но нельзя повторно объявить

Посмотрите на проблему с var на примере:

var offer = "The story ends";
var pill = blue;

if (pill == red ) {
 var offer = "Stay In Wonderland"; 
}
    
console.log(offer); // "Stay In Wonderland"

Итак, мы приняли синюю таблетку в глобальном масштабе, но когда мы напечатали предложение, нам пришлось остаться в стране чудес :(

Это потому, что var не имеет блочной области. Мы можем получить доступ к переменной offer внутри оператора if. И это изменяет присвоенное строковое значение offer.

Из-за различий в области видимости мы также получаем разные значения из циклов с замыканиями, как показано ниже:

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

Если вам недостаточно проблем, вот еще одна:

Мы можем обновить оба типа переменных, не получая ошибки:

С помощью let

let offer = "The story ends";
offer = "Stay in Wonderland";

С var

var offer = "The story ends";
offer = "Stay in Wonderland";

Но когда мы пытаемся повторно объявить их:

С помощью let

let offer = "The story ends";
let offer = "Stay in Wonderland"; // Uncaught SyntaxError: Identifier 'offer' has already been declared"

С переменная

var offer = "The story ends";
var offer = "Stay in Wonderland";

Мы бы не хотели непреднамеренно объявлять переменную два раза, верно?

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

Подъем

На этапе компиляции, всего за микросекунды до выполнения вашего кода, он сканируется на наличие объявлений функций и переменных. Все эти функции и объявления переменных добавляются в память внутри структуры данных JavaScript, которая называется Lexical Environment. Чтобы их можно было использовать еще до того, как они будут объявлены в исходном коде.

С помощью этой концепции мы можем использовать функции над строками, которые мы их определили:

mithrandir();  // "A wizard is never late"
mithrandir(){
  console.log('A wizard is never late');
}

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

Давайте посмотрим на пример:

С var

console.log(password); // undefined
var password = 'Speak, friend, and enter.';

С помощью let

console.log(password); // undefined
let password = 'Speak, friend, and enter.'; // Uncaught ReferenceError: Cannot access 'password' before initialization"

Закрытия

Есть подсказка для доступа к внутренним областям, и это использование замыкания!

Замыкание — это функция, которая обращается к своей внутренней области видимости, даже если она выполняется за пределами своей лексической области видимости.

Изучите концепцию замыкания на одном простом примере:

function ring() {
 let carrier = 'Frodo';
 function gollum() {
  console.log(carrier); // "Frodo"
 }
 gollum();
}

ring(); 

У нас есть переменная с именем carrier, и этой переменной присвоено имя «Frodo». Как и ожидалось, он доступен даже из внутренней функции gollum :)

Когда функция ring() выполняется, она выполняет функцию gollum(), и мы можем получить доступ к переменной Carrier вне области действия функции ring().

Голлум - это закрытие здесь.

Это для этой темы. Спасибо за чтение.

Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Получите эксклюзивный доступ к возможностям написания и советам в нашем сообществе Discord.