How JS Runs Code: Execution Context & The Call Stack
Quick Overview
The engine reserves memory for every declaration before it runs a single line, then executes. That two-phase order is why a `function` is callable above its definition while a `var` reads `undefined` there.
Deep Dive
1. Inside the Execution Container
In JavaScript, everything happens inside an Execution Context. Think of an Execution Context as a sealed container where JavaScript evaluates and executes your code.
2. Why It Matters
Understanding the 2 execution phases explains hoisting, scope boundaries, closures, call stack overflows, and how the V8 engine manages memory.
3. The 2 Execution Phases
Whenever a JavaScript program runs or a function is invoked, the engine creates an Execution Context in two distinct phases:
- Phase 1: Memory Creation Phase: The engine scans the code line-by-line before executing anything. It allocates memory space for variables (
varset toundefined) and stores full function declarations. - Phase 2: Code Execution Phase: The engine runs the code sequentially, assigning actual values to variables and executing function calls.
4. The Call Stack
The JS engine manages execution contexts using a LIFO (Last-In, First-Out) Call Stack. The Global Execution Context (GEC) sits at the bottom of the stack. When a function is called, a new local Execution Context is pushed onto the stack. When the function returns, its context is popped off!
[ Call Stack ]
┌─────────────────────────┐
│ calculateTax() Context │ <- Currently executing (Top of stack)
├─────────────────────────┤
│ processOrder() Context │
├─────────────────────────┤
│ Global Context (GEC) │ <- Bottom of stack
└─────────────────────────┘
5. Code Execution Step-by-Step
var x = 2;
function square(num) {
var ans = num * num;
return ans;
}
var square2 = square(x);
- Phase 1 (Memory):
x: undefined,square: [function code],square2: undefined. - Phase 2 (Execution):
x = 2. Callingsquare(2)creates a new local context. - Inside
squarelocal context:num = 2,ans = 4.return 4pops local context off stack and assigns4tosquare2.
6. Common Mistakes
- Stack Overflow: Recursive functions without a proper base condition push endless execution contexts onto the stack until it throws
RangeError: Maximum call stack size exceeded.
7. Try It Yourself
Write it yourself. The tests below run against your code.
Calculate Total with Nested Function Calls
Examples
8. Next Steps
Remember: Memory allocation happens BEFORE code execution. This leads directly to our next topic: Hoisting!
Next Up in JavaScript Engine & Core Language
Data Types in JavaScript: Primitives vs. Objects
Primitives copy by value and objects copy by reference, so `b = a` duplicates a number but shares an object. That single distinction explains most surprise mutations in a codebase.
Variables: const, let & var (The Box Metaphor)
`const` prevents reassignment, not mutation: a `const` object's properties can still be changed. `let` is scoped to its block, while `var` is function-scoped and leaks out of loops.
Strings & Template Literals `${}`
Only backticks interpolate: `${user}` reads the variable, while the identical text in quotes prints literally. Strings are immutable, so every method returns a new one rather than editing in place.