TGViewer
8BitJS 8BitJS @eightbitjs · 162 subscribers
Post #15 422
​​Hoisting в JavaScript: let и function

В первой части мы разобрали, как работает hoisting у var: переменная получает undefined ещё на этапе создания контекста, и поэтому доступ к ней до присвоения не вызывает ошибку. Теперь давайте посмотрим, чем отличается поведение let.

let

function demoLet() {
console.log(b) // [1]
let b = 10 // [2]
console.log(b) // [3]
}


Байткод функции (сокращённо, с пометками строк):

[generated bytecode for function: demoLet]
;; Инициализация переменной b
@0 : LdaTheHole
@1 : Star0

;; [1] console.log(b)
@2 : LdaGlobal [0]
@5 : Star2
@6 : GetNamedProperty r2, [1]
@10 : Star1
@11 : Ldar r0
@13 : ThrowReferenceErrorIfHole [2]
@15 : CallProperty1 r1, r2, r0

;; [2] let b = 10
@20 : LdaSmi [10]
@22 : Star0

;; [3] console.log(b)
@23 : LdaGlobal [0]
@26 : Star2
@27 : GetNamedProperty r2, [1]
@31 : Star1
@32 : CallProperty1 r1, r2, r0

;; Завершение функции
@37 : LdaUndefined
@38 : Return

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


Разбор:

- @0 LdaTheHole — слот b помечается как TheHole (TDZ).
- @1 Star0 — сохранить TheHole в r0.
- @2…@15 — первая попытка обращения к b, выбрасывается ReferenceError на шаге @13.
- @20 LdaSmi [10] — загрузить константу 10.
- @22 Star0 — присвоение r0 = 10 (инициализация b).
- @23…@32 — второй вызов console.log(b), теперь r0 = 10.
- @37 LdaUndefined — подготовка значения возврата.
- @38 Return — возврат из функции.

TDZ

Механизм Temporal Dead Zone (TDZ) реализован через несколько этапов:

- На первом этапе переменная получает специальное значение TheHole с помощью инструкции LdaTheHole. Это внутренний маркер движка V8, который обозначает «неинициализированное лексическое связывание (binding)». В отличие от undefined, это значение не доступно из JavaScript напрямую.

Любопытно, в байткоде инициализация let и var выражается загрузкой служебных констант (LdaTheHole и LdaUndefined) в аккумулятор и фактически должны иметь равную цену.

- При обращении к такой переменной движок выполняет ThrowReferenceErrorIfHole. Если в слоте всё ещё лежит TheHole, выбрасывается синхронный ReferenceError.
- После инициализации значение TheHole в слоте заменяется на реальное значение.

Таким образом, TDZ гарантирует, что доступ к let/const до инициализации невозможен, и это обеспечивается связкой LdaTheHole + ThrowReferenceErrorIfHole.

function

functionDecl(); [1]
function functionDecl() {
console.log("functionDecl ran"); [2]
}


Байткод верхнего уровня:

@6  : CallRuntime [DeclareGlobals], r1-r2
@11 : LdaGlobal [1]
@14 : Star1
@15 : CallUndefinedReceiver0 r1

Constant pool (size = 2)
0: <FixedArray[2]>
1: <String[12]: #functionDecl>


Разбор:

- @6 CallRuntime [DeclareGlobals] — на глобальном уровне регистрируются все var и Function Declarations.
- @11 LdaGlobal [1] — загрузить функцию из глобального объекта.
- @14 Star1 — запись функции в регистр r1
- @15 CallUndefinedReceiver0 r1 — выполнить вызов без явного this.

Байткод для самой функции functionDecl:

[generated bytecode for function: functionDecl]
@0 : LdaGlobal [0]
@3 : Star1
@4 : GetNamedProperty r1, [1]
@8 : Star0
@9 : LdaConstant [2]
@11 : Star2
@12 : CallProperty1 r0, r1, r2
@17 : LdaUndefined
@18 : Return

Constant pool:
0: <String[7]: #console>
1: <String[3]: #log>
2: <String[16]: #functionDecl ran>


Разбор:

- @0 LdaGlobal [0] — загрузить глобальный объект console.
- @3 Star1 — сохранить console в r1 (receiver).
- @4 GetNamedProperty r1 [1] — получить свойство log у console. acc = console.log.
- @8 Star0 — сохранить функцию console.log в r0 (callee).
- @9 LdaConstant [2] — загрузить строковую константу "functionDecl ran".
- @11 Star2 — сохранить аргумент в r2.
- @12 CallProperty1 r0, r1, r2 — вызвать console.log (callee = r0, receiver = r1, arg = r2).
- @17 LdaUndefined — подготовить значение возврата.
- @18 Return — возврат из функции functionDecl.

To Be Countinue.. в следующий раз подробнее посмотрим на реализацию TDZ в v8

---
#JavaScript #Hoisting #V8 #TDZ #TemporalDeadZone #Interview #8BitJS
  • 🔥 5
  • ❤ 2
More from @eightbitjs
  1. Jul 21, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 5. Зачем здесь WebAssembly После реализации бак…
  2. Jul 18, 2026​​Итоги CodeRun Summer: 15 задач и 636 попыток решения Финал CodeRun Summer Challenge выгл…
  3. Jul 17, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 4. Кэш, бакеты и упаковка событий В предыдущей…
  4. Jul 16, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 3. Как оставить четыре байта и не сломать ответ…
  5. Jul 15, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 2. Четыре байта, которые могут изменить результ…
  6. Jul 14, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 1. Разностный массив — это только начало На про…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →