Поднятие и TDZ
Вернёмся к квартире с комнатами. Прежде чем кто-то войдёт в квартиру, JavaScript обходит её и заранее расставляет по комнатам коробки с именами всех объявленных там переменных. Поэтому переменная может существовать раньше, чем выполнится строка с её объявлением.
Переменную var можно прочитать выше строки, где она объявлена, и программа не остановится. Первая строка этой функции печатает undefined, хотя total объявлена ниже:
function order() {
console.log(total); // undefined
var total = 10;
console.log(total); // 10
}
order();Давайте разберём вызов order() по шагам:
- Ещё до первой строки функции переменная
totalуже существует и хранитundefined. - Первый
console.log(total)печатаетundefined. - Строка
var total = 10кладёт вtotalчисло10. - Второй
console.log(total)печатает10.
Если на третьей строке написать let total = 10, та же функция остановится с ошибкой. Разница в том, что JavaScript делает с объявлениями до того, как выполнит первую строку.
Что поднимается
В теме 5 вы видели, что объявление функции можно вызвать выше его текста — это поднятие. На деле JavaScript заранее создаёт все переменные, объявленные в функции или файле, а не только функции. Перед вызовом функции он просматривает её тело, а перед запуском файла — весь файл.
Здесь картинка с коробками и само слово «поднятие» неточны: ни одна строка программы никуда не переезжает. Объявления остаются на своих местах в тексте, JavaScript только обрабатывает их раньше остальных строк.
Что именно готово заранее, зависит от вида объявления.
Объявления функций
Объявление функции поднимается целиком, вместе с телом. Поэтому функцию можно вызвать выше её текста.
Переменные var
Переменная var поднимается без значения: с первой строки функции она уже существует и хранит undefined. Значение 10 она получает, только когда выполняется строка var total = 10.
Поднимается объявление, а присваивание остаётся на своём месте. Переменная var из ветки if или из цикла поднимается к началу всей функции, ведь блоков var не замечает.
Временная мёртвая зона
Переменные let и const тоже создаются заранее, в начале своего блока, но до своей строки недоступны. В нашей картинке их коробка заклеена, и открыть её можно только на строке объявления. Участок от начала блока до строки объявления называют временной мёртвой зоной, коротко TDZ.
Нажмите «Следующий шаг» и следите за рамкой order: total в ней есть с первого шага вызова, а count получает значение только на своей строке.
1function order() {2 console.log(total);3 var total = 10;4 console.log(total);5 let count = 3;6 console.log(count);7}8 9order();
Скрипт вызывает order и ждёт, пока вызов закончится.
Если обратиться к переменной в мёртвой зоне, программа остановится:
function order() {
console.log(count); // ✗
let count = 3;
}
order();ReferenceError: Cannot access 'count' before initializationТак это сообщение пишут Node.js и Chrome. Оно отличается от count is not defined: имя уже существует, но значения ещё не получило. То же сообщение выдаёт выражение функции, записанное в const, если вызвать его выше этой строки: константа уже существует, но функции ещё не хранит.
Эта ошибка видна сразу, а undefined от var проходит незаметно и портит вычисления дальше по коду.
На собеседовании часто спрашивают: «Что такое поднятие и временная мёртвая зона?» Хороший ответ называет три случая:
- объявление функции готово к вызову до первой строки;
varсуществует заранее и до своей строки хранитundefined;letиconstсуществуют заранее, но до своей строки обращение к ним даётReferenceError.
Частая ошибка
Переменную var использовали в вычислении выше её строки:
function price() {
const total = base * 2; // ✗
var base = 50;
return total;
}
console.log(price());Программа печатает NaN, и ошибки нет. На строке с total переменная base уже существует, но хранит undefined, а undefined * 2 даёт NaN. Перенесите объявление base выше вычисления.
Если объявить base через let или const, программа остановится на этой же строке с ReferenceError.
Главное
- JavaScript создаёт переменные функции или файла заранее, до первой строки, и это называют поднятием.
- Объявление функции поднимается целиком, поэтому функцию можно вызвать выше её текста.
varдо своей строки хранитundefined, а присваивание выполняется на своём месте.letиconstдо своей строки находятся во временной мёртвой зоне: обращение к ним даётReferenceError.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.