JavaScript Execution Context: How Code Runs

By Mohiuddin Murad
October 17, 2023
JavaScript
Execution Context
Hoisting
Call Stack
JavaScript Execution Context: How Code Runs

The Execution Context is the environment in which JavaScript code is evaluated and executed. Every Execution Context has two main components: the Variable Environment (which stores variables, function declarations, and arguments) and the Thread of Execution (which executes the code line by line).

Types of Execution Context:

  1. Global Execution Context (GEC): When the JavaScript engine starts running code, it creates a Global Execution Context. This is the default context. The GEC establishes two key things: a global object (window in browsers, global in Node.js) and the this keyword, which initially points to the global object.
  2. Function Execution Context (FEC): Whenever a function is called, a new Function Execution Context is created and placed on top of the Call Stack. Each function call gets its own separate context.

Phases of Execution Context Creation:

An Execution Context is created in two distinct phases:

  1. Creation Phase (Memory Allocation): In this phase, the engine allocates memory for variable and function declarations before executing any code. Variables declared with var are initialized with undefined, while function declarations are stored in memory in their entirety. This behavior is known as Hoisting. Variables declared with let and const are also hoisted but remain in a "temporal dead zone" and are not initialized.
  2. Execution Phase (Code Execution): During this phase, the code is executed sequentially. The engine assigns the actual values to variables. When a function is called, a new Execution Context is created and pushed onto the Call Stack, and this two-phase process repeats for the function's code.

How the Call Stack Works:

The Call Stack is a LIFO (Last-In, First-Out) data structure that manages and tracks Execution Contexts.

function third() {
  console.log("Inside third");
}

function second() {
  third();
  console.log("Inside second");
}

function first() {
  second();
  console.log("Inside first");
}

first();

State of the Call Stack:

  1. When first() is invoked, its FEC is pushed onto the stack.
  2. Inside first, second() is called, and its FEC is pushed on top.
  3. Inside second, third() is called, and its FEC is pushed on top.
  4. Once third finishes, its context is popped off the stack.
  5. Execution returns to second, which then finishes and is popped off.
  6. Finally, first finishes and is popped off, leaving the stack empty.

This stack-based approach is fundamental to controlling the execution flow of all JavaScript code.