Skip to Content
JavaScriptОбласть видимости

Область видимости и hoisting

Уровень: middle.

Вопрос

Чем let отличается от var, и что такое temporal dead zone?

Короткий ответ

var имеет функциональную область видимости и всплывает: объявление поднимается в начало функции и до присваивания значение undefined. let и const имеют блочную область, {}. Обратиться к ним до строки объявления нельзя: промежуток от начала блока до инициализации называется temporal dead zone, доступ бросает ReferenceError.

const запрещает переприсвоить связывание. Содержимое объекта при этом менять можно: константа держит ссылку, не глубокую заморозку.

Функциональное объявление function foo() {} всплывает целиком, её можно вызвать выше по коду функции. const foo = () => {} всплывает как let: до строки инициализации вызова нет.

Разбор

Цикл с var и setTimeout печатает одно и то же конечное значение: на все итерации одна переменная. let в заголовке for создаёт связывание на итерацию, колбэк видит своё i.

Блоки if и for для let — отдельные области. Для var они прозрачны, переменная видна во всей функции. В строгом режиме повторное let в том же блоке — ошибка, повторный var молча сливается.

Модуль ES сам по себе в строгом режиме. Переменная модуля не становится свойством window. Скрипт без type="module" и без 'use strict' ещё может положить var в глобальный объект.

На что смотрят на senior

Hoisting — про порядок связывания имён при создании окружения, не про то, что движок физически переставляет строки. Инициализация let остаётся на своей строке, поэтому TDZ и существует.

Замыкание держит переменную, не значение на момент создания функции. Это стык с темой замыканий: колбэк, созданный в цикле, читает переменную позже.

Типичные ошибки

  • Считать, что const делает объект неизменяемым.
  • Объяснять var блочной областью.
  • Думать, что стрелочная функция всплывает так же, как function declaration.
  • Ловить TDZ через try/catch вокруг обычного кода вместо переноса объявления выше.

Задача

Что напечатает цикл и почему второй console.log упадёт?

for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0) } { console.log(n) let n = 1 }
Показать ответ

Таймеры увидят одну переменную i. К моменту их запуска цикл уже закончился, три раза выведется 3.

let n внутри блока в зоне temporal dead zone до своей строки. console.log(n) бросит ReferenceError, а не напечатает undefined.

Last updated on