How JavaScript Code is Executed: Execution Context & The Call Stack
Every single line of JavaScript code runs inside an Execution Context. Understanding how the JavaScript engine creates, executes, and destroys these execution contexts behind the scenes—and how it manages their order using the Call Stack—is foundational to mastering the language.
1. The Core Premise: The Execution Context Structure
Before executing a single line of code, the JavaScript engine creates a container called the Execution Context. Think of it as a big container with two distinct internal components:
-
Memory Component (Variable Environment):
- Stores all variables, parameters, and function declarations as key-value pairs.
- Example:
key: valuepairs liken: undefinedorsquare: { ...code... }.
-
Code Component (Thread of Execution):
- The space where the actual code is parsed and executed line by line, one command at a time.
Note: JavaScript is a synchronous, single-threaded language. It executes one line of code at a time in a specific top-to-bottom sequence.
2. Benchmark Code Example
To understand this execution model step-by-step, we will track how the JavaScript engine processes the following program:
var n = 2;
function square(num) {
var ans = num * num;
return ans;
}
var square2 = square(n);
var square4 = square(4);
When you hit "Run," JavaScript does not simply start executing code immediately. Instead, it creates a Global Execution Context (GEC) and processes the program in two distinct phases.
3. Phase 1: Memory Creation Phase (Creation Phase)
Before executing any code, JavaScript skims through the entire script top-to-bottom to allocate memory for all variables and function declarations.
Allocation Rules During Phase 1:
-
Variables (
var):- JS reserves a space in memory for the identifier.
- It assigns a special placeholder value called
undefined. - Result:
nis set toundefined,square2toundefined, andsquare4toundefined.
-
Functions (
function declaration):- JS reserves a space in memory for the function identifier.
- Instead of storing
undefined, it copies and stores the entire literal source code of the function inside the memory block. - Result:
squarepoints to the complete function blockfunction square(num) { ... }.
Summary of Phase 1 State Table:
4. Phase 2: Code Execution Phase
Once memory allocation is complete, JavaScript runs through the code a second time. This is the Code Execution Phase, where values are assigned, calculations are computed, and functions are invoked.
Execution Progression:
Line 1: var n = 2;
- Engine assigns value 2 to identifier 'n' in memory.
- Memory: n: 2
Line 2 - 5: function square(num) { ... }
- Nothing to execute here (declaration already stored in Phase 1).
- Engine skips to Line 6.
Line 6: var square2 = square(n);
- Function Invocation encountered!
- A brand-new Local Execution Context is instantiated for square(n).
5. Function Invocation & Nested Execution Context Creation
In JavaScript, functions are like mini-programs. Whenever a function is invoked (recognized by function name followed by parentheses ()), JavaScript pauses the current context and creates an altogether new Execution Context dedicated exclusively to that function call.
Invoking square(n) on Line 6:
Here, n (value 2) is passed as an argument to the function parameter num.
Key Terminology:
- Parameter: The placeholder identifier defined in the function declaration (
num).- Argument: The actual runtime value passed into the function call (
nwhich evaluates to2).
6. Returning Control & Context Destruction
When the engine hits the return statement inside a function context:
- Value Handoff: The local variable value in
ans(4) is sent back to the parent execution context where the function was originally invoked (Line 6). - Assignment: In the Global Execution Context memory,
square2updates fromundefinedto4. - Context Destruction: The entire Local Execution Context for
square(n)is completely deleted from memory.
7. Subsequent Invocations (Executing Line 7)
After Line 6 completes, the global code execution thread proceeds to Line 7:
var square4 = square(4);
Since this is another function invocation, JavaScript creates another brand-new Local Execution Context.
- Value
16is assigned to variablesquare4in the Global Execution Context memory. - Local Execution Context 2 is completely deleted.
- All code in the script is now finished.
- The Global Execution Context itself is finally deleted from memory.
8. Managing Execution Control: The Call Stack
With execution contexts constantly being created, nested, and destroyed, how does the JavaScript engine keep track of what context is currently executing and where to return control?
It uses the Call Stack (also known as the Execution Context Stack).
Real-Life Analogy
Think of a Call Stack like a stack of cafeteria trays. You can only place a tray on top (Push), and you can only remove the tray currently sitting on top (Pop). The tray at the very bottom is the base context that stays until all tasks are complete.
Visualizing the Call Stack Lifecycle for our Benchmark Program:
Call Stack Operation Steps:
- Global Base: When a JavaScript program starts, the engine pushes the Global Execution Context (GEC) onto the bottom of the stack.
- Context Pushing: Whenever a function is invoked, a new execution context is pushed onto the top of the Call Stack. The engine always executes whatever context is at the top of the stack.
- Context Popping: When a function returns or completes execution, its context is popped off the top of the Call Stack, and control returns to the underlying context below it.
- Completion: When the script finishes executing all code, the GEC is popped off, leaving the Call Stack completely empty.
9. Terminology Matrix: Call Stack Aliases
In technical documentation, articles, and interview scenarios, the Call Stack is referred to by various names. All of the following terms refer to the exact same mechanism:
Summary and Key Takeaways
- Execution Context: The fundamental environment where JavaScript code is evaluated and executed. It contains two parts: the Memory Component (Variable Environment) and the Code Component (Thread of Execution).
- Single-Threaded Synchronous Nature: JavaScript executes one line of code at a time, in a specific top-to-bottom sequence.
- Two Execution Phases:
- Memory Creation Phase: JavaScript scans the code, sets variable identifiers to
undefined, and copies function declarations entirely into memory. - Code Execution Phase: JavaScript runs code line by line, assigns actual values, and computes function calls.
- Memory Creation Phase: JavaScript scans the code, sets variable identifiers to
- Function Execution Context: Invoking a function creates a brand-new, isolated local Execution Context.
- Parameters vs. Arguments: Parameters are variable placeholders in the function definition (
num); arguments are actual values passed during execution (2or4). - Context Deletion: Returning from a function sends the return value to the outer context and immediately destroys that function's execution context.
- Call Stack: The engine's internal stack data structure used to manage execution context creation, nesting order, and deletion. The Call Stack maintains the strict operational control flow of any JavaScript application.