How JavaScript Executes Code: Understanding Execution Context and the Call Stack
JavaScript is a single-threaded, synchronous programming language at its core, but it handles asynchronous operations through clever mechanisms like the event loop. To truly understand how JavaScript works under the hood, you need to grasp two fundamental concepts: execution context and the call stack. The JavaScript Execution Context An execution context is an abstract concept that holds information about the environment where JavaScript code is executed. There are three types of execution contexts in JavaScript: Global Execution Context: Created when the JavaScript engine first starts executing your code Function Execution Context: Created whenever a function is invoked Eval Execution Context: Created inside an eval function (rarely used) Each execution context has two phases: Creation phase Execution phase Creation Phase During the creation phase, the JavaScript engine: Creates the Variable Object (VO) which contains: Function arguments (for function contexts) Inner variable declarations (but not their assignments) Function declarations Creates the scope chain Determines the value of this This explains hoisting - why you can use functions before they're declared in your code, but variables will be undefined until their actual assignment. Execution Phase During the execution phase: The code is executed line by line Variables are assigned values Functions are executed when called The Call Stack The call stack is a data structure that keeps track of function calls in your program. It works on the LIFO principle (Last In, First Out). Here's how it works: When the JavaScript engine starts executing your script, it creates a global execution context and pushes it onto the call stack When a function is called, a new execution context for that function is created and pushed onto the stack When a function returns, its execution context is popped off the stack The engine continues executing the current context (now the one below in the stack) function first() { console.log("Inside first function"); second(); console.log("Back to first function"); } function second() { console.log("Inside second function"); third(); console.log("Back to second function"); } function third() { console.log("Inside third function"); } first(); console.log("Global execution"); The call stack for this code would look like: third() is pushed when called from second() second() is pushed when called from first() first() is pushed when called from global Global execution context is always at the bottom When third() completes, it's popped off, then second(), then first(), and finally the global context remains until the program ends. Visualizing the Process Let's examine a concrete example: var a = 2; function square(num) { var result = num * num; return result; } var squareOfTwo = square(a); console.log(squareOfTwo); Execution steps: Global Execution Context Creation: Creates variable object with: a: undefined square: squareOfTwo: undefined Sets up scope chain Determines this (window in browsers) Global Execution: Assigns a = 2 Reaches square(a) call: Creates new execution context for square Creation phase for square: num: 2 (from argument) result: undefined Execution phase for square: Calculates result = 2 * 2 Returns 4 square context is popped from call stack Assigns return value to squareOfTwo = 4 Calls console.log(4) Creates context for console.log Executes it Pops it from stack Program completes, global context is popped Handling Asynchronous Code While the call stack handles synchronous operations, asynchronous operations (like setTimeout, promises, or AJAX calls) are handled by the browser APIs and the event queue. The event loop constantly checks if the call stack is empty, and if so, pushes the next callback from the queue to the stack. This separation is why JavaScript can handle asynchronous operations despite being single-threaded. Key Takeaways JavaScript executes code by creating and managing execution contexts Each context has creation and execution phases The call stack tracks the execution order of functions Understanding these concepts explains hoisting, scope, and closure behaviors Asynchronous operations are handled outside the main call stack I believe this has been both helpful and insightful to all Javascript developers.Until some other time, happy coding

JavaScript is a single-threaded, synchronous programming language at its core, but it handles asynchronous operations through clever mechanisms like the event loop. To truly understand how JavaScript works under the hood, you need to grasp two fundamental concepts: execution context and the call stack.
The JavaScript Execution Context
An execution context is an abstract concept that holds information about the environment where JavaScript code is executed. There are three types of execution contexts in JavaScript:
- Global Execution Context: Created when the JavaScript engine first starts executing your code
- Function Execution Context: Created whenever a function is invoked
-
Eval Execution Context: Created inside an
eval
function (rarely used)
Each execution context has two phases:
- Creation phase
- Execution phase
Creation Phase
During the creation phase, the JavaScript engine:
- Creates the Variable Object (VO) which contains:
- Function arguments (for function contexts)
- Inner variable declarations (but not their assignments)
- Function declarations
- Creates the scope chain
- Determines the value of
this
This explains hoisting - why you can use functions before they're declared in your code, but variables will be undefined
until their actual assignment.
Execution Phase
During the execution phase:
- The code is executed line by line
- Variables are assigned values
- Functions are executed when called
The Call Stack
The call stack is a data structure that keeps track of function calls in your program. It works on the LIFO principle (Last In, First Out).
Here's how it works:
- When the JavaScript engine starts executing your script, it creates a global execution context and pushes it onto the call stack
- When a function is called, a new execution context for that function is created and pushed onto the stack
- When a function returns, its execution context is popped off the stack
- The engine continues executing the current context (now the one below in the stack)
function first() {
console.log("Inside first function");
second();
console.log("Back to first function");
}
function second() {
console.log("Inside second function");
third();
console.log("Back to second function");
}
function third() {
console.log("Inside third function");
}
first();
console.log("Global execution");
The call stack for this code would look like:
-
third()
is pushed when called fromsecond()
-
second()
is pushed when called fromfirst()
-
first()
is pushed when called from global - Global execution context is always at the bottom
When third()
completes, it's popped off, then second()
, then first()
, and finally the global context remains until the program ends.
Visualizing the Process
Let's examine a concrete example:
var a = 2;
function square(num) {
var result = num * num;
return result;
}
var squareOfTwo = square(a);
console.log(squareOfTwo);
Execution steps:
-
Global Execution Context Creation:
- Creates variable object with:
a: undefined
square:
squareOfTwo: undefined
- Sets up scope chain
- Determines
this
(window in browsers)
- Creates variable object with:
-
Global Execution:
- Assigns
a = 2
- Reaches
square(a)
call:- Creates new execution context for
square
- Creation phase for
square
: -
num: 2
(from argument) result: undefined
- Execution phase for
square
: - Calculates
result = 2 * 2
- Returns
4
-
square
context is popped from call stack
- Creates new execution context for
- Assigns return value to
squareOfTwo = 4
- Calls
console.log(4)
- Creates context for
console.log
- Executes it
- Pops it from stack
- Creates context for
- Assigns
Program completes, global context is popped
Handling Asynchronous Code
While the call stack handles synchronous operations, asynchronous operations (like setTimeout, promises, or AJAX calls) are handled by the browser APIs and the event queue. The event loop constantly checks if the call stack is empty, and if so, pushes the next callback from the queue to the stack.
This separation is why JavaScript can handle asynchronous operations despite being single-threaded.
Key Takeaways
- JavaScript executes code by creating and managing execution contexts
- Each context has creation and execution phases
- The call stack tracks the execution order of functions
- Understanding these concepts explains hoisting, scope, and closure behaviors
- Asynchronous operations are handled outside the main call stack
I believe this has been both helpful and insightful to all Javascript developers.Until some other time, happy coding