Давайте узнаем, почему в 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.