Всплытие (hoisting) и TDZ
Всплытие (hoisting) и TDZ
Технический фундамент инициализации окружения
Перед исполнением кода движок готовит окружение:
- регистрирует объявления в области видимости;
- по-разному инициализирует
var,let,const,function; - создает TDZ для блочных объявлений до строки инициализации;
- из-за этого "обращение до объявления" может давать разные результаты.
Понимание этого этапа убирает ощущение "странной магии" и делает ошибки предсказуемыми.
Почему hoisting и TDZ часто ломают ожидания
Ты пишешь код сверху вниз и ожидаешь, что он именно так и выполнится. Но JavaScript сначала проходит фазу подготовки области видимости, и часть объявлений становится известной заранее. Из-за этого появляются сюрпризы: где-то undefined, где-то ReferenceError.
Ключевой момент: hoisting это не "магическое перемещение строк", а особенность этапа инициализации окружения выполнения.
Проверь себя: почему одинаковое обращение "до объявления" работает по-разному для var и let?
var и hoisting
console.log(count); // undefined
var count = 3;
Для var объявление известно заранее, но инициализация значением происходит на строке присваивания. Поэтому до нее получаешь undefined, а не значение.
Здесь часто путаются: "почему не ошибка?" - потому что имя уже зарегистрировано в области видимости.
let/const и TDZ
console.log(score); // ReferenceError
let score = 10;
Для let и const действует TDZ (Temporal Dead Zone) - временная зона, где переменная уже "существует" в scope, но к ней нельзя обращаться до строки инициализации.
const дополнительно требует инициализации сразу.
const limit = 70; // ок
// const limit; // SyntaxError
Смотри, что важно: TDZ защищает от использования переменной до корректной подготовки значения.
Еще один частый сюрприз: TDZ может сработать из-за shadowing внутри блока — даже если снаружи переменная с таким именем есть.
const mode = 'prod';
if (true) {
// console.log(mode); // ReferenceError из-за TDZ локального mode
const mode = 'test';
}
Hoisting функций
Function Declaration всплывает целиком, поэтому ее можно вызвать до объявления.
sayHello(); // works
function sayHello() {
console.log('Hello');
}
Но Function Expression в переменной подчиняется правилам переменной.
// greet(); // ReferenceError/TypeError в зависимости от объявления
const greet = function () {
console.log('Hi');
};
Смотри, что важно: если greet объявлен через var, то до присваивания он будет undefined, и вызов greet() даст TypeError. А если через let/const, получишь ReferenceError из TDZ.
Проверь себя: почему declaration и expression ведут себя по-разному при вызове "до объявления"?
Мини-сценарий: инициализация состояния
Представь модуль страницы:
- сначала читается конфиг;
- потом вычисляется режим;
- затем рендерится экран.
Если в этом потоке обратиться к let-переменной до инициализации, экран упадет до рендера. Именно поэтому порядок инициализации критичен.
const mode = getMode(config);
const config = { isPreview: true }; // TDZ ошибка, если так написать
Важно: здесь упадет на чтении config, поэтому getMode(...) даже не успеет вызваться.
Правильный порядок:
const config = { isPreview: true };
const mode = getMode(config);
Частые ошибки новичков
- Смешивать
varиletв одном стиле кода. - Полагаться на "работает из-за hoisting" вместо явного порядка.
- Вызывать function expression до объявления.
- Игнорировать TDZ-ошибки как "странность языка".
Анти-провал: объявляй переменные до использования и не строй логику на побочных эффектах hoisting.
Что будет, если изменить входные данные
Если заменить var на let в старом коде с обращением до объявления, ты получишь ReferenceError. Это хорошо: ошибка становится явной и помогает исправить порядок инициализации, а не прятать проблему в undefined.
Проверь себя: почему явная ошибка в этом случае полезнее "тихого" undefined?
Краткий итог
hoistingсвязан с фазой подготовки окружения выполнения.varдоступен до присваивания какundefined.let/constимеют TDZ и не дают обращаться к переменной до инициализации.- Function Declaration всплывает целиком, Function Expression - нет.
- Лучший практический подход: явный порядок объявлений и минимум зависимости от hoisting-поведения.