Перейти к содержимому
Теория · Область видимости · урок 3 из 8

Поднятие и TDZ

Вернёмся к квартире с комнатами. Прежде чем кто-то войдёт в квартиру, JavaScript обходит её и заранее расставляет по комнатам коробки с именами всех объявленных там переменных. Поэтому переменная может существовать раньше, чем выполнится строка с её объявлением.

Переменную var можно прочитать выше строки, где она объявлена, и программа не остановится. Первая строка этой функции печатает undefined, хотя total объявлена ниже:

JavaScript
function order() {
    console.log(total); // undefined
    var total = 10;
    console.log(total); // 10
}

order();

Давайте разберём вызов order() по шагам:

  1. Ещё до первой строки функции переменная total уже существует и хранит undefined.
  2. Первый console.log(total) печатает undefined.
  3. Строка var total = 10 кладёт в total число 10.
  4. Второй 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 получает значение только на своей строке.

ВживуюЧто готово в начале вызовашаг 1 из 8 · рамок: 1

Скрипт вызывает order и ждёт, пока вызов закончится.

Рамка order показывает переменные вызова с его первой строки

Если обратиться к переменной в мёртвой зоне, программа остановится:

JavaScript
function order() {
    console.log(count); // ✗
    let count = 3;
}

order();
TEXT
ReferenceError: Cannot access 'count' before initialization

Так это сообщение пишут Node.js и Chrome. Оно отличается от count is not defined: имя уже существует, но значения ещё не получило. То же сообщение выдаёт выражение функции, записанное в const, если вызвать его выше этой строки: константа уже существует, но функции ещё не хранит.

Эта ошибка видна сразу, а undefined от var проходит незаметно и портит вычисления дальше по коду.

На собеседовании часто спрашивают: «Что такое поднятие и временная мёртвая зона?» Хороший ответ называет три случая:

  • объявление функции готово к вызову до первой строки;
  • var существует заранее и до своей строки хранит undefined;
  • let и const существуют заранее, но до своей строки обращение к ним даёт ReferenceError.

Частая ошибка

Переменную var использовали в вычислении выше её строки:

JavaScript
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 и подпишись на канал — откроются все уроки.

Войти другим способом