Hoisting: поднятие объявлений

Hoisting — поведение JavaScript-движка, при котором объявления переменных и функций «поднимаются» в начало своей области видимости до выполнения кода.

Зачем нужно

Hoisting объясняет, почему можно вызвать функцию до её объявления в коде, и почему обращение к переменной var до присвоения возвращает undefined, а не ошибку. Знание этого механизма помогает избегать неочевидных багов и понимать разницу между var, let, const и объявлениями функций.

Где используется

  • Отладка неочевидного поведения кода с var
  • Понимание порядка инициализации в модулях
  • Объяснение Temporal Dead Zone для let/const
  • Написание кода без зависимости от порядка объявлений

Основной контент

Поднятие function declaration

// Можно вызвать до объявления — весь код функции поднимается
sayHello; // 'Привет' — работает!

function sayHello() {
  console.log('Привет');
}

// Function expression — НЕ поднимается
// greet; // TypeError: greet is not a function
const greet = function {
  console.log('Привет');
};

Поднятие var

// var объявление поднимается, но не инициализация
console.log(x); // undefined (не ReferenceError!)
var x = 5;
console.log(x); // 5

// Эквивалентно такому коду:
var x;           // поднято
console.log(x);  // undefined
x = 5;
console.log(x);  // 5

Temporal Dead Zone (TDZ) для let и const

// let и const тоже поднимаются, но НЕ инициализируются
// Обращение до объявления — ReferenceError

// console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;

// Temporal Dead Zone — промежуток от начала блока до let/const
{
  // TDZ начинается здесь
  // console.log(z); // ReferenceError
  let z = 20; // TDZ заканчивается
  console.log(z); // 20
}

var в циклах

// var имеет функциональную область — один i для всех итераций
for (var i = 0; i < 3; i++) {
  setTimeout( => console.log(i), 0);
}
// Выводит: 3, 3, 3

// let создаёт новую переменную для каждой итерации
for (let j = 0; j < 3; j++) {
  setTimeout( => console.log(j), 0);
}
// Выводит: 0, 1, 2

Порядок поднятия

// Порядок: function declarations > переменные
var x = 1;
function x() {} // function declaration поднимается выше var

// После поднятия x = 1 (переменная перезаписывает функцию при инициализации)
console.log(typeof x); // 'number'

Частые ошибки

  • Использование переменной var до присвоения — получаем undefined, а не значение. Переходите на let/const.
  • Ожидание поднятия const/let — TDZ вызывает ReferenceError. Объявляйте переменные в начале блока.
  • Function expression ≠ function declaration — стрелочные функции и функции в const не поднимаются полностью.

Связанные темы

Ресурсы


⚡ Источник: ⎡spec03⎦ Hoisting согласно официальной спецификации · AsForJS

  • 📅 2023-11-19 · YouTube
  • Тезисы:
    • Термина "hoisting" / "всплытие" в спецификации ECMAScript НЕТ — это жаргон из учебников
    • В спецификации есть Hoistable Declaration, но он описывает совсем не то, что подразумевают под "всплытием"
    • Реальный механизм: 3 фазы выполнения (parsing → instantiation → execution). На фазе instantiation создаются bindings, var связывается с undefined, let/const остаются неинициализированными (hole)
    • Идентификатор всегда Reference — отсюда ReferenceError для любых проблем
    • Function declaration реально доступна сверху — это специальное правило спеки, а не "всплытие"
  • Цитата: «Я беру на себя ответственность за каждое сказанное слово. Все декларируемое строго соответствует спецификации»

⚡ Источник: JavaScript and the Lexical Environment · AsForJS

  • 📅 2023-05-12 · YouTube
  • Тезисы: Lexical Environment — это запись Identifier→Binding на момент выполнения. То, что называют "hoisting", — это фаза HoistableDeclaration при создании Environment Record