Why JavaScript Doesn’t Execute Code Top-to-Bottom ?
When I started learning JavaScript, hoisting felt like a bug pretending to be a feature.

Why didn’t this throw an error?
Why does let behave differently? (We'll study this later in another blog)
And why do functions work even before they’re defined?
The answer to all of this lives in Execution Context.
Once I understood how JavaScript prepares code before running it, hoisting stopped being confusing and started making sense.
Let's break it down.
Hoisting happens in the creation phase, not during the execution.
What is Hoisting?
Hoisting is JavaScript’s behavior of moving declarations to the top of their scope during the creation phase.
Not physically moving code — but allocating memory before execution starts.
Let's understand hoisting for var and functions with an example:

At first glance, this code looks wrong.
- We are calling printMessage() before defining it.
- We are logging x before assigning it.
- We are even logging the function itself. Yet… this code works.
Why and How?
What You Expect
Error, because x is declared later.
What Actually Happens
JavaScript stores the variable with a special keyword undefined.
Why?
During the creation phase, JavaScript does this:

Then during execution:
![]()
Key Rules for var
- var is hoisted
- Memory is allocated
- Initialized with undefined
- Scoped to function, not block
This is why var feels “weird” — but it’s actually consistent once you understand execution context.
Why Functions Are Fully Hoisted
This is where things get interesting.
During the creation phase:

- JavaScript allocates memory
- Stores the entire function definition
So in memory:
JavaScript already knows what printMessage() is before execution begins. This is why function declarations are fully hoisted.
Now JavaScript starts executing the code from top to bottom.
Line 1

JavaScript finds the full printMessage() function in memory.
Output:
![]()
Line 2

JavaScript looks for x in memory.
- x exists
- Its value is undefined
Output:
> Important
> This is not an error because x was already created during the creation phase.
Line 3

Since printMessage points to the entire function, JavaScript prints the function itself.
Output:

Line 5

Now, during execution, x gets its actual value.
Memory update:
Now hoisting works little different for function expression.

TypeError: printMessage is not a function
![]()
Why?
Because only the variable is hoisted, not the function.
Creation phase:

This is why it throws an error.
For arrow functions it works in the same way how function expression behaves.
Finally, Hoisting is not magic.
It’s a side effect of how JavaScript creates execution contexts.
Once you understand:
- Creation phase
- Memory allocation
- TDZ
- Scope rules
Hoisting becomes predictable, logical, and honestly… kind of elegant.
If You like my content and want to connect with me, connect me on LinkedIn.


Top comments (0)