Hoisting In JavaScript
What Actually Happens Behind the Scenes.

Up until now, you’ve learned how to write JavaScript that stores data, performs operations, makes decisions, and organises logic using functions. But there is something deeper happening behind everything you’ve written so far. Something that explains why certain code works even when it logically shouldn’t. Something that confuses almost every beginner at some point.
That concept is hoisting.
Why This Topic Matters.
Let’s start with something that looks strange.
console.log(a);
var a = 10;
What do you expect this to print?
Most people would say this should throw an error because we are using a before declaring it. But JavaScript prints:
undefined
Now look at this:
console.log(b);
let b = 20;
This throws an error.
Same pattern. Different result.
Why?
That is exactly what hoisting explains.
What is Hoisting?
Hoisting is JavaScript’s behaviour of moving declarations to the top of their scope before code execution. But this definition alone is not enough. Because hoisting does not behave the same for everything. Different types of declarations behave differently when hoisted. To understand hoisting properly, you need to understand how JavaScript executes code.
How JavaScript Executes Code (Mental Model).
JavaScript does not run your code line by line in a single pass.
It runs in two phases:
Creation Phase
Execution Phase
Creation Phase
Before executing your code, JavaScript scans the entire file and sets up memory.During this phase:
Variables are registered
Functions are stored in memory
Scope is determined
But no code is executed yet.
Execution Phase
Now JavaScript starts running the code line by line. This is where values are assigned and functions are executed.
Understanding Hoisting with var
console.log(a);
var a = 10;
During the creation phase:
var a = undefined;
During execution:
console.log(a); // undefined
a = 10;
So var is:
hoisted
initialised with undefined
Understanding Hoisting with let and const
console.log(b);
let b = 20;
During the creation phase:
b is hoisted but not initialised
During execution:
Accessing b before initialization throws an error.
This is where the Temporal Dead Zone comes in.
Temporal Dead Zone (TDZ)
The Temporal Dead Zone is the time between:
When a variable is hoisted and when it is initialized During this time, accessing the variable results in a ReferenceError.
console.log(b); // Error
let b = 20;
Think of TDZ as a restricted zone.The variable exists, but you cannot access it yet.
Quick Comparison
| Keyword | Hoisted | Initialized | Accessible Before Declaration |
|---|---|---|---|
| var | Yes | Yes (undefined) |
Yes |
| let | Yes | No | No (TDZ) |
| const | Yes | No | No (TDZ) |
Hoisting with Functions
Now this is where things get interesting.
Function Declaration
greet();
function greet() {
console.log("Hello");
}
This works.
Why?
Because function declarations are fully hoisted.
During creation phase:
function greet() {
console.log("Hello");
}
So you can call it before it appears in the code.
Function Expression
greet();
const greet = function() {
console.log("Hello");
};
This throws an error.
Because it behaves like a variable.
So:
const greet; // TDZ
greet(); // Error
Why Does JavaScript Behave Like This?
JavaScript was designed to be flexible. But older behavior like var caused many bugs because accessing variables before assignment returned undefined.
To fix this, modern JavaScript introduced:
let
const
Temporal Dead Zone
These prevent accidental usage of variables before initialization.
Common Confusion
1. "Hoisting means moving code to top"
Not exactly.
JavaScript does not physically move code. It just allocates memory before execution.
2. "let and const are not hoisted"
Incorrect.
They are hoisted, but not initialized. That is why TDZ exists.
Hoisting Inside Functions
Hoisting works inside functions as well.
function test() {
console.log(a);
var a = 10;
}
test();
Output:
undefined
Because inside the function:
var a = undefined;
Shadowing and Hoisting
function test(){
console.log(a);
var a = 20;
}
test ();
Output:
undefined
Because the inner a shadows the outer a.
Hoisting with let inside block
let a = 10;
function test(){
console.log(a);
let a = 20;
}
test();
This throws an error.
Because a is in TDZ inside the function.
Real Mental Model
Instead of thinking hoisting is confusing, think like this:
JavaScript first prepares everything
Then it executes
During preparation:
vargets default value undefinedletandconststay uninitializedfunctions are fully stored
Real-World Example
function calculate() {
console.log(total);
var total = 100;
if (true) {
let discount = 20;
console.log(total - discount);
}
}
calculate();
Output:
undefined
80
Understanding hoisting explains why this works.
Common Mistakes
Assuming variables behave the same
Using
varunintentionallyIgnoring TDZ
Calling function expressions before definition
Best Practices
Prefer
letandconstovervarDeclare variables at the top of the scope
Avoid relying on hoisting behaviour
Write predictable code.
Final Thought
Hoisting is not just a concept. It is a reflection of how JavaScript actually runs your code. If you understand hoisting properly, many confusing behaviours in JavaScript stop being confusing. Instead of guessing what the code will do, you start knowing.
And that is the difference between writing code and understanding it.






