При изучении JavaScript первое, что мы слышим о переменных JS, это то, что var
поднимается, тогда как let
и const
не поднимаются.
Но так ли это на самом деле? Технически НЕТ.
Миф # 1
Подъем: движок JS физически размещает переменные в верхней части кода во время выполнения.
Согласно MDN: концептуально, например, строгое определение подъема предполагает, что объявления переменных и функций физически перемещаются в верхнюю часть вашего кода, но на самом деле это не то, что происходит. Вместо этого объявления переменных и функций помещаются в память на этапе компиляции, но остаются там, где вы ввели их в код.
Миф 2.
Подняты var
переменных. let
и const
нет.
Технически все переменные JS подняты. Где
let
иcost
отличается отvar
в процессе подъема, находится в части инициализации.
Почему мы получаем ReferenceError
при доступе к переменным 'let'
перед их объявлением? 🤔
Жизненный цикл переменной
Предварительное информирование: движок JavaScript интерпретирует ваш код в два отдельных этапа. Фаза компиляции и фаза выполнения. да. Вы не ослышались. JS компилируется.
Двигаясь дальше, переменная JS технически имеет 3 фазы.
- Фаза объявления: переменная регистрируется в области действия.
- Этап инициализации: переменная инициализируется с помощью
undefined
. - Этап присвоения: переменной присваивается значение.
Примечание. Не путайте этап объявления и объявление переменной: этап объявления выполняется компилятором JS, а объявление переменной - это явный код, написанный пользователем, т.е. var foo;
Жизненный цикл var
- Этап объявления: время компиляции.
- Этап инициализации: время компиляции.
- Этап назначения: время выполнения.
Для var
фаза инициализации выполняется сразу после фазы объявления. И оба они выполняются во время компиляции.
Сравнивая этот пример с фазовой диаграммой,
- Фаза объявления + фаза инициализации:
foo
добавляется в область и автоматически присваиваетсяundefined
в качестве значения во время компиляции. - Казнь начинается.
- JS проверяет
foo
в области и находит его значение какundefined.
- Фаза присвоения:
foo
присваивается значение 2.
Жизненный цикл аренды
- Этап объявления: время компиляции.
- Этап инициализации: время выполнения.
- Этап назначения: время выполнения.
Для let
фаза объявления выполняется во время компиляции. Но в отличие от var
, этап инициализации выполняется не сразу после этапа объявления, а во время выполнения.
В начале фазы выполнения переменные let
объявляются, но не инициализируются. Эти переменные инициализируются только тогда, когда движок JS достигает ключевого слова let
.
До тех пор считается, что переменная находится в временной мертвой зоне.
Получение или установка неинициализированных переменных, находящихся в TDZ, приведет к
ReferenceError
.
Сравнивая этот пример с фазовой диаграммой,
- Фаза объявления: foo добавляется в область видимости во время компиляции.
- Казнь начинается. Foo в настоящее время не инициализирован и не имеет никакого значения, даже
undefined
. Доступ к foo в этот момент даетReferenceError
. - Этап инициализации: когда механизм JS обнаруживает ключевое слово
let
, foo назначаетсяundefined
. - Фаза присвоения: foo присваивается значение 2.
Фактически, несколько других операторов также ведут себя аналогично let. Например:
const
,class
TL;DR
- Технически
var
,let
иconst
подняты. var
объявляется и инициализируется во время подъема.let
иconst
объявляются только во время подъема, но не инициализируются. Доступ к неинициализированным переменным приводит кReferenceError
.- По возможности предпочитайте 38_ 39, чтобы избежать путаницы, связанной с подъемом.
- По возможности предпочитайте
const
let
, чтобы иметь строгий контроль над переменными, которые не должны изменяться.