- Hoisting is a JavaScript mechanism where the engine creates a variable environment during compilation, registering all variable and function declarations in memory.
- For beginners, it’s often helpful to think of these declarations as being “moved to the top” of their containing scope, but technically the engine just sets up the environment before execution begins.
- This allows you to reference variables and functions before they are declared — but the behavior depends on whether you’re using
var,let,const, or function declarations.
How JavaScript actually runs code
When JS executes, it doesn’t just run line by line immediately. The JS engine (like V8 in Chrome or Node.js) does two phases for each scope:
1. Creation Phase
- The engine scans the code.
- Creates a variable environment for the scope.
- Registers all variable and function declarations.
-
Sets up memory bindings:
-
var→undefined -
let/const→ exist in Temporal Dead Zone (TDZ) - function declarations → full function object in memory
-
2. Execution Phase
- Runs the code line by line.
- Assigns values to variables.
- Calls functions.
Hoisting in Action
Var example
console.log(a); // undefined
var a = 10;
-
Creation phase:
aexists in memory →undefined -
Execution phase:
a = 10runs
Let / Const example
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;
console.log(c); // ReferenceError: Cannot access 'c' before initialization
const c = 30;
-
TDZ: period from the start of scope until initialization. Accessing
let/constin TDZ throws a ReferenceError.
Function declarations
foo(); // Works
function foo() {
console.log("Hoisted function!");
}
- Functions are hoisted with their entire body, so you can call them before they appear in code.
Quick Table: Hoisting by Declaration Type
| Declaration | Hoisted? | Initialization Timing | Accessible before initialization? |
|---|---|---|---|
var |
Yes |
undefined at compile time |
Yes (undefined) |
let |
Yes (binding exists) | Only when execution reaches declaration | No — TDZ |
const |
Yes (binding exists) | Must be initialized at declaration | No — TDZ |
function |
Yes (entire function hoisted) | Immediately available | Yes |
What the Engine Actually Does
Global Execution Context Lifecycle
Global Execution Context
│
├─ Creation Phase
│ ├─ var → undefined
│ ├─ let/const → TDZ (cannot be accessed)
│ └─ function → full function object
│
└─ Execution Phase
├─ Assign values
├─ Execute code line by line
└─ Create new Execution Contexts for functions
Common Pitfalls
-
Accessing
let/constbefore declaration
console.log(x); // ReferenceError
let x = 10;
-
Calling functions assigned to
varvs function declarations
console.log(fooVar); // undefined
var fooVar = function() { console.log("Hi"); };
fooFunc(); // Works
function fooFunc() { console.log("Hello"); }
- Thinking object literals affect hoisting
console.log(obj.a); // ReferenceError if obj uses let/const inside TDZ
Key Takeaways
- Hoisting happens before code executes.
-
var→ hoisted, initialized toundefined -
let/const→ hoisted, but in TDZ - Functions → fully hoisted
- Understanding hoisting prevents undefined errors and ReferenceErrors.
Top comments (0)