By the end, you will be able to explain why JavaScript declarations can be accessed before their written position, and how var, let, const, and function declarations differ.
What it is
Hoisting is JavaScript's behavior of processing declarations before executing code. A useful mental model is that the engine creates bindings for variables and functions at the top of their scope, then runs the code from top to bottom.
var declarations and function declarations are initialized during this phase. let and const declarations are also hoisted, but they remain uninitialized until their declaration is reached; this uninitialized period is called the temporal dead zone.
Related terms include scope, block scope, function scope, function declaration, function expression, and temporal dead zone.
Why it matters
- It explains why some code returns
undefinedinstead of throwing aReferenceError. - It helps you debug errors caused by using variables before they are ready.
- It shows why function declarations can be called before their definition.
- It clarifies why
letandconstare safer thanvarin many cases. - It prevents surprises when moving code around inside blocks, functions, or modules.
Syntax or steps
The smallest useful pattern is to separate declaration from assignment. A var declaration is processed before the assignment happens.
var a = 1;
// Conceptually behaves like:
var a;
a = 1;
For let and const, the binding exists before the declaration line, but reading it before initialization causes an error.
let b = 2;
// Conceptually:
let b;
b = 2;
Function declarations are fully available before their written position. Function expressions are not, because the variable binding is hoisted but the function value is assigned later.
Example
console.log(a); // undefined
var a = 1;
try {
console.log(b);
} catch (error) {
console.log(error.message);
}
let b = 2;
sayHello(); // Hello
function sayHello() {
console.log("Hello");
}
try {
console.log(greet);
} catch (error) {
console.log(error.message);
}
const greet = function () {
console.log("Hi");
};
Part by part:
console.log(a)printsundefinedbecausevar ais hoisted and initialized toundefined.console.log(b)throws aReferenceErrorbecauselet bis in the temporal dead zone until its declaration is reached.sayHello()works before the function definition because function declarations are hoisted with their function body.console.log(greet)throws becauseconst greetis hoisted but not initialized until the assignment line.
Common mistakes
- Assuming
letandconstbehave likevar. Fix: remember they are hoisted but uninitialized. - Using
varinside a block and expecting block scope. Fix: useletorconstfor block-scoped variables. - Calling a function expression before its assignment. Fix: define the function before using it, or use a function declaration.
- Thinking code is physically moved to the top. Fix: think of declarations being processed before execution, not rewritten.
When to use it
Hoisting is not something you choose; it is JavaScript behavior. The practical choice is whether to rely on it or write code that avoids surprises.
| Approach | When appropriate | Why |
|---|---|---|
| Use function declarations | Small helper functions in the same scope | They can be called before their definition |
Use let or const and define before use |
Most modern JavaScript code | Clearer behavior and fewer temporal dead zone surprises |
Use var |
Legacy code or intentional function scope | It hoists and initializes to undefined |
Practice
Guided exercise: predict the output, then run this code.
console.log(x);
var x = 5;
console.log(x);
Expected output:
undefined
5
Challenge: rewrite the example using let so the first log throws an error. Hint: replace var x = 5; with let x = 5; and wrap the first console.log(x) in a try block.
Quick check
Question: Why does console.log(a); var a = 1; print undefined instead of throwing a ReferenceError?
Answer: The var a declaration is hoisted and initialized to undefined, while the assignment a = 1 stays where it is written.
Summary
Hoisting means JavaScript processes declarations before executing code. var and function declarations are initialized early, while let and const are hoisted but remain uninitialized until reached.
Prefer explicit ordering and modern declarations to make code easier to read and debug.