ES6: Блочные области видимости
До введения стандарта ES6 основой всех областей видимости являлись функции. У любой функции существует своя область видимости. Проще всего это можно рассмотреть на примере:
Объявленные с помощью ключевого слова var
переменные внутри функции не влияют на переменные из других областей видимости, в том числе и глобальной. Именно на этом свойстве основана хорошая практика обворачивания всего кода в самовызывающуюся анонимную функцию (self-executing anonymous function):
Подобное решение позволяет полностью контролировать, какие переменные будут переданы в глобальное окружение. Тем не менее, данные правила работали исключительно с функциями, а на другие блочные конструкции не действовали:
Пример с циклом for
относительно безвреден и после последней итерации оставляет переменную i
. Подобное использование циклов широко распространено и, скорее всего, не станет причиной ошибки. Неочевидные вещи начинают появляться при использовании других блочных конструкций:
Очевидно, что после запуска данного кода будет создана переменная a
, содержащая в себе число 10
. Все становится не так очевидно, когда условие переданное в конструкцию if
не является правдивым:
Какой результат можно ожидать? Код внутри конструкции if
не запускался, а значит и перменная a
не была инициализирована. Логично предположить, что единственным возможным результатом является ошибка (попытка обратиться к несуществующей переменной обычно выдает ReferenceError
). Тем не менее, подобной ошибки не возникает, и в консоль выводится undefined
. Такое поведение объясняется поднятием переменных (hoisting). Конструкция, указанная выше, интерпретируется следующим образом:
Оператор let
С релизом стандарта ES6 появилась возможность создавать переменные, приуроченные к отдельным блокам. Это означает, что для создания лексического окружения (scope) достаточно просто обвернуть код в фигурные скобки: { ... }
:
Скорее всего, вы никогда не будете использовать конструкцию, показанную выше, но, тем не менее, она является абсолютно валидной и позволяет наглядно продемонстрировать создание нового лексического окружения.
Таким образом, при выполнении следующего кода переменная i
не будет доступна вне цикла:
Подобное поведение будет наблюдаться и в других блочных конструкциях:
Hoisting
При использовании ключевого слова let
происходит поднятие переменных (hoisting). Но сам процесс поднятия реализуется совершенно другим образом:
Запустив подобный пример вы скорее всего можете прийти к выводу, что поднятия не происходит. Это не так. Для того, чтобы убедиться в наличии поднятия достаточно объявить еще одну переменную вне блока:
Несмотря на то, что переменная b
была объявлена вне блока и, таким образом, должна быть доступна, результатом выполнения кода все равно является ReferenceError
. Подобное поведение называется “временной мёртвой зоной”.
При запуске кода из блока происходит резервирование имён всех переменных объявленных в любом месте блока. Таким образом, несмотря на наличие внешней переменной, результатом выполнения кода будет ReferenceError
. Чтобы избежать подобных ошибок, всегда объявляйте все используемые переменные в самом начале блока:
Временная мёртвая зона не распространяется на функции до первого их вызова:
Тем не менее, если вы попытаетесь вызвать функцию f
до того, как будет объявлена переменная num
, то всё равно получите ошибку:
Оператор const
Оператор const
, как и let
, работает с блочными областями видимости (также подвергается правилам временной мёртвой зоны) и предназначен для создания констант - переменных, для которых доступно только чтение после их инициализации:
Новое присваивание значения переменной num
выведет ошибку. Таким образом, значение, записанное в переменную при её инициализации, невозможно изменить с помощью присваивания. Создание новых переменных с таким же именем также выведет ошибку:
Заново инициализировать переменную с помощью оператора const
тоже не получится:
Константы непостоянны
На первый взгяд, может показаться, что всё, что было записано в константу невозможно изменить. Но, на самом деле, нельзя менять только литерал или ссылку:
Чтобы сделать константу, содержащую объект, настоящей константой слудует использовать Object.freeze()
.
Ссылки по теме
- Перевод статьи ES6 Let, Const and the “Temporal Dead Zone” (TDZ) in Depth (ES6: Let, Const и «Временная мёртвая зона» (ВМЗ) изнутри) от css-live.ru
- Временная мёртвая зона
- Object.freeze()
- Константы в JavaScript − когда они нужны и где их использовать
Комментарии