Три фазы выполнения JavaScript

Выполнение JS-кода идёт в три фазы: parsing → instantiation → execution. На фазе instantiation создаются bindings (отсюда эффект "hoisting").

Что это / Зачем

Объясняет почему:

  • var-переменная доступна до своего объявления (с undefined)
  • let/const дают ReferenceError в TDZ
  • function declaration вызывается выше по коду
  • Существует JIT-warmup (Ignition → TurboFan)

Три фазы

1. Parsing (Compilation)

  • Лексер → AST
  • Проверка синтаксиса (SyntaxError здесь)
  • Резолв scope: для каждой функции/блока строится список deklaraций
  • V8: байткод Ignition + предварительные данные для TurboFan

2. Instantiation (Creating Execution Context)

Создаётся Execution Context с Lexical Environment. Для каждого declaration:

Declaration Действие на этапе instantiation
var x binding xundefined
function f binding f → готовая функция
let x binding x → uninitialized (hole)
const x binding x → uninitialized (hole)
class C binding C → uninitialized (hole)

3. Execution

Построчное выполнение. При обращении к идентификатору:

  • Если binding = uninitialized → ReferenceError
  • Если есть значение → используется
  • При let x = 5 или const x = 5 — binding инициализируется значением

Пример

console.log(a); // undefined  (var = bound to undefined)
console.log(b); // ReferenceError (let = hole)
console.log(f); // ƒ f {...} (function declaration)
// console.log(g); // TypeError (g is undefined, .call on undefined)

var a = 1;
let b = 2;
function f() {}
var g = function {};

На фазе instantiation:

  • a → undefined
  • b → hole
  • f → готовая функция
  • g → undefined (только var-binding, не expression)

JIT-warmup в V8 (поверх 3 фаз)

После прохождения 3 фаз код исполняется Ignition (интерпретатор байткода). Если функция вызывается часто:

  1. Ignition собирает feedback (типы аргументов, результаты)
  2. При достижении threshold — TurboFan компилирует в машинный код
  3. Если предположения нарушаются — деоптимизация обратно в Ignition

Подводные камни

  • function declaration внутри блока (не верхний уровень) ведёт себя как let в новых движках, но как var в legacy
  • function f() {} и let f = function {} — РАЗНЫЕ declarations на этапе instantiation
  • В strict mode правила инициализации строже (например, дубликаты параметров запрещены)

🎓 Источники

  • ⚡ [⎡JSbook 04.00⎦ Как выполняется JS код] · AsForJS · 2023-09-30 · YouTube
    • Тезисы: разбор execution в 3 фазах. Parsing, instantiation, execution. Каждая фаза — отдельный шаг в спеке
  • ⚡ [⎡spec03⎦ Hoisting согласно спецификации] · AsForJS · 2023-11-19 · YouTube
    • Тезисы: то что называют "hoisting" — это фаза instantiation. На ней создаются bindings ДО выполнения. Никакого "поднятия" в тексте программы нет
  • ⚡ [⎡spec 00⎦ Call Stack согласно спецификации] · AsForJS · 2023-11-03 · YouTube
    • Тезисы: Call Stack хранит Execution Context, который создаётся на фазе instantiation для каждой функции

См. также