IT-ХОЗЯЕВА
579 subscribers
62 photos
5 videos
53 links
Канал сообщества boosty.to/jointime
Download Telegram
Forwarded from 8BitJS
​​Hoisting в JavaScript: миф о «поднятии» или реальная механика движка

Как часто на собеседованиях вам задавали классический вопрос: «Что такое hoisting?»

Не растерявшись, мы обычно отвечаем: «Это поднятие переменных и функций наверх их области видимости». Интервьюер одобрительно кивает, и мы идём дальше.

Но действительно ли движок переписывает код и «перемещает» объявления? На самом деле это лишь метафора, упрощающая объяснение, но не отражающая реальную механику. В этой статье разберём, что говорит об этом спецификация ECMAScript и как это реализовано во внутренностях V8.

Если открыть учебники и статьи, почти всегда можно встретить объяснение в стиле: «JavaScript поднимает объявление переменной или функции в начало области видимости». Пример из таких источников:

function foo() {
console.log('1:', a)
a = 42
console.log('2:', a)
var a
}

// 1: undefined
// 2: 42


Затем идёт иллюстрация «как будто движок переписал код» и добавил объявление в начало:

function scope() {
var a // hoisting
console.log('1:', a)
a = 42
console.log('2:', a)
}


TL;DR

Сегодня разберём:

- Hoisting — это не перенос строк кода, а ранняя регистрация привязок до исполнения.
- var создаётся в контексте и инициализируется undefined.
- let/const регистрируются, но попадают в TDZ (Temporal Dead Zone) до инициализации (ранний доступ → ReferenceError).
- Function Declarations поднимаются в виде готовых функций (их можно вызывать до места объявления).
- В V8 это реализовано через вызов Runtime::kDeclareGlobals и видно по инструкциям байткода (LdaTheHole, ThrowReferenceErrorIfHole).

---

Концептуальный разбор процессов

JS‑движок выполняет код в две стадии:

Creation Phase

Фаза создания Execution Context. Иногда её называют Memory Creation Phase или Compile Phase. Во время этой фазы:

- создаются Execution ContextVariable Environment и Lexical Environment;

- для var создаются mutable bindings и сразу инициализируются undefined;

- для let/const создаются bindings, но они остаются неинициализированными (значение the‑hole, TDZ);

- Function Declarations получают готовый объект функции.

Execution Phase

Фаза построчного выполнения кода.

Важно: термины фаз — это лишь распространённые формулировки. В спецификации описаны алгоритмы вроде FunctionDeclarationInstantiation и операции с Environment Records (CreateMutableBinding, InitializeBindingCreateImmutableBinding и т.д.).

Примеры кода и байткод V8

Ниже рассмотрим, как это выглядит в байткоде.

Важно: в байткоде вы не всегда увидите явное LdaUndefined для var. Ignition при создании кадра (frame) заранее заполняет регистры и слоты значением undefined.

var

Пример:

function demoVar() {
console.log(a) // [1]
var a = 10 // [2]
console.log(a) // [3]
}


Байткод функции (индексы слотов опущены для простоты):

[generated bytecode for function: demoVar]
@0 : LdaGlobal [0]
@3 : Star2
@4 : GetNamedProperty r2, [1]
@8 : Star1
@9 : CallProperty1 r1, r2, r0
@14 : LdaSmi [10]
@16 : Star0
@17 : LdaGlobal [0]
@20 : Star2
@21 : GetNamedProperty r2, [1]
@25 : Star1
@26 : CallProperty1 r1, r2, r0
@31 : LdaUndefined
@32 : Return

Constant pool:
0: <String[7]: #console>
1: <String[3]: #log>


Разбор:

@0 LdaGlobal [0] — загрузить из constant pool console.

@3 Star2 — сохранить в регистр r2.

@4 GetNamedProperty r2, [1] — получить свойство log. acc = console.log

@8 Star1 — сохранить функцию в r1.

@9 CallProperty1 r1, r2, r0 — вызвать console.log(a). В регистре r1 мы храним функцию console.log, а в r2 reciever console (аналог this для вызова). Так как регистр r0 (переменная a) ещё не инициализирован в теле, он равен undefined.

@14 LdaSmi [10] — загрузить число 10 в аккумулятор

@16 Star0 — сохранить в r0, инициализация a = 10.

@17…@26 — повторный вызов console.log(a), теперь r0 = 10.

@31 LdaUndefined — подготовка значения возврата по умолчанию.

@32 Return — возврат из функции.

To be continue...

---
#JavaScript #Hoisting #V8 #ExecutionContext #TDZ #TemporalDeadZone #Interview #8BitJS
6❤‍🔥11👍52🔥2