Hoisting isn't magic — it's the result of how the JavaScript engine processes code in two phases: compilation (creation) and execution. This article goes deeper than the beginner-level explanation to understand what the engine actually does with execution contexts, environment records, and the Temporal Dead Zone.
Prerequisites: JS Foundations #1 — Variables, Scope & Hoisting
1. The Two Phases — Creation vs Execution
Every time the engine enters a scope (global, function, block), it creates an execution context in two phases:
Loading editor...
2. Environment Records — Where Variables Actually Live
The spec defines three kinds of environment records:
Loading editor...
3. The Temporal Dead Zone — Why It Exists
The TDZ exists because let/const hoist the binding but not the initialization:
Loading editor...
The key insight: typeof is NOT safe for TDZ variables:
Loading editor...
4. Function Declarations in Blocks — The Surprising Case
Function declarations inside blocks behave differently in strict vs sloppy mode:
Loading editor...
5. Class Hoisting — Classes are NOT Fully Hoisted
class declarations are hoisted like let — binding exists in TDZ until the class body runs:
Loading editor...
6. Summary — Hoisting Rules by Declaration Type
Loading editor...
Key Takeaways
- Execution contexts are created in two phases: creation (hoisting, scope setup) and execution (code runs line by line).
varhoists withundefinedinitialization;let/consthoist but stay uninitialized (TDZ).- TDZ is temporal, not spatial — based on execution order within the block.
typeofon a TDZ variable throwsReferenceError— the one case wheretypeofis not safe.- Function declarations in blocks are a spec anomaly — avoid them in all modes.
classhoists likelet— must be defined before use.
Next: Modules — IIFE, CommonJS, AMD, ESM — the evolution of JavaScript module systems.
