⬅ Previous Topic
JavaScript Lexical ScopeJavaScript Hoisting
Variables, Functions, and Scope Mysteries Unraveled
Introduction to JavaScript Hoisting
Ever wondered why you can use a variable before it's defined in JavaScript? That's because of a concept called hoisting. At its core, hoisting is JavaScript's way of setting up memory before it starts running your code — quietly and invisibly behind the scenes.
What is Hoisting?
Hoisting is JavaScript’s default behavior of moving declarations
to the top of the current scope (either function or global). But it doesn't move initializations.
It’s like the engine says, “Let me prepare all the variable and function names I’ll need before I begin.”
Variable Hoisting with var
Variables declared with var
are hoisted — their declarations, not their initializations.
console.log(x);
var x = 5;
undefined
Why undefined
and not a ReferenceError?
Behind the scenes, the JavaScript interpreter rewrites it like this:
var x;
console.log(x);
x = 5;
This means the var
is hoisted, but its 5
isn't — hence undefined
.
Variable Hoisting with let
and const
Things get stricter with let
and const
. Yes, they are hoisted, but they live in the Temporal Dead Zone (TDZ) from the start of the block until the declaration is encountered.
console.log(y);
let y = 10;
ReferenceError: Cannot access 'y' before initialization
Here’s what you need to know:
let
andconst
are hoisted.- But they are not initialized.
- Accessing them before declaration throws an error.
Function Hoisting
Function declarations are fully hoisted — both their name and body.
greet();
function greet() {
console.log("Hello there!");
}
Hello there!
JavaScript reads this as if the function was defined before the call. However, function expressions are a different story.
Function Expressions are NOT Hoisted (Well, Not Like You Think)
sayHi();
var sayHi = function() {
console.log("Hi!");
}
TypeError: sayHi is not a function
What happened here? sayHi
is hoisted as a var
— which means it’s set to undefined
— not as a function. So calling it throws an error.
Hoisting in Functions and Block Scope
Let's peek into a function to see hoisting at work:
function test() {
console.log(a);
var a = 20;
}
test();
undefined
Why? Because var a
is hoisted to the top of the function scope, just like this:
function test() {
var a;
console.log(a);
a = 20;
}
Block Scope and Hoisting
What happens inside a block?
{
console.log(msg);
let msg = "Block scope hoisting!";
}
ReferenceError: Cannot access 'msg' before initialization
Even though let msg
is hoisted to the top of the block, it’s still uninitialized — welcome to the TDZ.
Hoisting Order: A Summary
Type | Hoisted? | Initialized? | Accessible Before Declaration? |
---|---|---|---|
var | Yes | Yes (to undefined) | Yes |
let | Yes | No | No (TDZ error) |
const | Yes | No | No (TDZ error) |
Function Declaration | Yes | Yes | Yes |
Function Expression | Only variable name | No | No (TypeError) |
Best Practices to Avoid Hoisting Confusion
- Always declare variables at the top of their scope.
- Use
let
andconst
to prevent silent hoisting bugs. - Declare functions before you call them — even though declarations are hoisted, it improves readability.
Conclusion
Hoisting is one of those quirks that give JavaScript its charm — and sometimes, its confusion. But once you understand the rules, especially the differences between var
, let
, const
, and functions, you’ll be well-equipped to write cleaner, more predictable code.
Remember, JavaScript is not reading your code top-down as you wrote it — it’s reordering it in memory before it runs. Once you embrace that mindset, hoisting becomes less magical and more logical.
⬅ Previous Topic
JavaScript Lexical Scope