Have you ever hit “Run” on your JavaScript code and wondered — okay, but what is actually happening right now?
Most tutorials skip this part. They teach you what to write, but not what happens inside when the code runs. And honestly, that one gap causes a lot of confusion later — weird undefined values, functions working before they’re defined, and errors that just don’t make sense.
Once you understand what happens when JavaScript code runs, a lot of that confusion disappears on its own.
Let’s go through it step by step.
What Is a JavaScript Engine?
JavaScript doesn’t run directly on your computer. It needs a JavaScript engine to process and execute it.
The most popular one is V8 — built by Google and used in Chrome and Node.js. Firefox uses SpiderMonkey. Safari uses JavaScriptCore.
When you run any JavaScript file, the engine takes over. And the very first thing it does is create something called an Execution Context.
What Is a JavaScript Execution Context?
Think of an execution context as a workspace — a container where JavaScript keeps everything it needs to run your code.
Every JavaScript program starts with a Global Execution Context (GEC). This is the default environment created automatically when your script starts.
Each execution context has two parts:
- Memory Component — stores all variables and functions
- Code Component — runs your code line by line
JavaScript is single-threaded, meaning it does one thing at a time. No multitasking. It reads your code from top to bottom, in order.
The Two Phases of JavaScript Execution
Here’s something most beginners don’t know:
Before running a single line of code, JavaScript reads your entire file first.
It does this in two phases:
Phase 1 — Memory Creation Phase
In this phase, JavaScript scans your code and sets up memory for all variables and functions.
- Variables are stored as
undefined - Functions are stored with their full definition
No code runs in this phase. JavaScript is just preparing.
Example:
var name = "Alice";
var age = 25;
function greet() {
console.log("Hello, " + name);
}
After the memory creation phase, memory looks like this:
name → undefined
age → undefined
greet → { full function stored }
This is why you can call a function before it appears in your file and it still works — because by the time any code runs, the function is already in memory.
This behavior is called Hoisting.
Phase 2 — Code Execution Phase
Now JavaScript goes back to line 1 and actually runs the code.
This time, it assigns real values to variables and executes function calls one by one.
var name = "Alice"; // name: undefined → "Alice"
var age = 25; // age: undefined → 25
function greet() {
console.log("Hello, " + name);
}
greet(); // function is called here
When greet() is called, JavaScript doesn’t just run it inline. It creates a new execution context just for that function — with its own memory phase and execution phase. When the function finishes, that context is removed.
What Is the JavaScript Call Stack?
So now we have multiple execution contexts being created and destroyed. JavaScript needs a way to track all of this. That’s where the Call Stack comes in.
The call stack is like a to-do list of execution contexts. It works as a stack — last in, first out (LIFO).
Example:
function sayHello() {
console.log("Hello!");
}
function greet() {
sayHello();
}
greet();
Here’s what happens on the call stack:
- Global Execution Context is added first
greet()is called → its context is pushed onto the stack- Inside
greet(),sayHello()is called → pushed onto the stack sayHello()finishes → popped off the stackgreet()finishes → popped off the stack- Program ends → Global Context is removed
[ sayHello() ] ← currently running
[ greet() ]
[ Global ]
Quick tip: That “Maximum call stack size exceeded” error you’ve seen? That means a function kept calling itself endlessly and the stack ran out of space. Now you know exactly why it happens.
Full Example — Everything Together
Let’s trace through a complete example so you can see all three things — execution context, memory phases, and call stack — working together.
var num = 5;
function square(n) {
var result = n * n;
return result;
}
var answer = square(num);
console.log(answer); // 25
Step-by-step breakdown:
- Global Execution Context is created
- Memory Phase:
num → undefined,square → [function],answer → undefined - Execution Phase starts:
numbecomes5 square(num)is called → new execution context created, pushed to stack- Inside
square— Memory Phase:n → undefined,result → undefined - Inside
square— Execution Phase:n = 5,result = 25, returns25 square‘s context is destroyed, popped off the stack- Back in global:
answerbecomes25 console.log(answer)prints25
Every function call is its own little world. It lives, does its job, and disappears.
Watch the Full Video Explanation
If you prefer to see this visually, I’ve covered this entire topic in a detailed video with diagrams and animations:
🎥 Watch here: https://youtu.be/xAS0wPcs4HY
Why Does This Matter? (Practical Reasons)
Understanding how JavaScript works internally isn’t just theory. Here’s why it helps you every day:
- Hoisting makes sense — Variables are
undefinedbefore assignment, not missing. Functions work before their definition. You’ll stop being surprised by this. - Closures become obvious — Closures are built on execution contexts. Once you understand contexts, closures are just a natural extension.
- Debugging becomes faster — When you know when variables get values, you can trace bugs much more precisely.
- Async JavaScript is less confusing — The event loop, callbacks, promises, and async/await are all built on top of the call stack. This is the base you need.
Quick Summary
| Concept | What It Means |
|---|---|
| JavaScript Engine | V8, SpiderMonkey — they process your code |
| Execution Context | A container where JS runs your code (Memory + Code) |
| Memory Creation Phase | Variables set to undefined, functions stored in full |
| Code Execution Phase | Code runs line by line, real values are assigned |
| Call Stack | Tracks active execution contexts (LIFO order) |
| Hoisting | Variables/functions are available before assignment because of Phase 1 |
Interview-Ready Answer
Q: What happens when JavaScript code runs?
When JavaScript code runs, the engine first creates a Global Execution Context. This context has two phases. In the Memory Creation Phase, all variables are stored as undefined and functions are stored with their complete definition. In the Code Execution Phase, JavaScript runs the code line by line, assigning actual values. When a function is called, a new execution context is created and pushed onto the Call Stack. When the function returns, that context is removed. This continues until the program finishes.
What’s Next?
Now that you understand what happens when JavaScript code runs, you’re ready to go deeper.
The next topics that make complete sense once you know this:
- Hoisting — why variables and functions behave the way they do
- Closures — how functions remember their outer variables
- The Event Loop — how async code like setTimeout really works
- Scope Chain — how JavaScript looks up variable values
Check out the full video series here 👉 https://youtu.be/xAS0wPcs4HY
If this helped you, drop a comment below and let me know which part clicked for you. And if you’re preparing for JavaScript interviews, bookmark this — it comes up more often than you’d think.
Happy coding! 🚀