Execution Context in JavaScript

MMuhammad Naeem
November 11, 2025
6 min read
73 views

1. What Is an Execution Context?

In simple terms, an Execution Context (EC) is the environment where JavaScript code runs. It is responsible for managing how variables and functions are allocated, stored, and executed.

If you’ve ever wondered:

  • Why can I access some variables but not others?

  • Why does this behave differently in different places?

  • What’s really happening during “hoisting”?

  • How do closures keep access to variables even after the outer function ends?

The answer to all of these lies in Execution Contexts.


2. What are the phases of an Execution Context?

Every execution context goes through two key phases:

1. Memory Creation Phase (Initialization)

  • JavaScript allocates memory for all variables and functions.

  • Variables are initialized with undefined.

  • Function declarations are stored completely (their full code) in memory.

2. Code Execution Phase

  • The JavaScript engine executes the code line by line.

  • Variables get their assigned values.

  • When a function is called, a new execution context is created and added to the call stack.

Real-World Analogy

Imagine you’re a chef preparing a dish:

  • Creation Phase → You gather ingredients, prepare your workspace, and set up tools.

  • Execution Phase → You start cooking: chopping, mixing, frying, plating, etc.

The kitchen here is your execution context, the environment where everything happens.


3. How many types of Execution Contexts are there?

1. Global Execution Context (GEC)

  • Created automatically when the JavaScript program starts.

  • Represents the outermost scope.

  • All global variables and functions live here.

  • It’s the first context created and the last to be destroyed.

2. Function Execution Context (FEC)

  • Every time a function is called, a new execution context is created.

  • Each function call gets its own isolated environment for variables and inner functions.

  • When the function finishes:

    • Its execution context is popped off the call stack.

    • Control returns to the previous context.


call stack

4. The Call Stack, How JavaScript Manages Contexts

The Call Stack is a data structure (LIFO: Last-In, First-Out) that keeps track of all active execution contexts.

Example Flow

  1. Global Execution Context is created first and sits at the bottom of the stack.

  2. When a function is called, its Function Execution Context is pushed on top.

  3. When the function finishes, it’s popped off the stack.

function A() {
  console.log("Inside A");
  B();
}

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

A();

Call Stack flow:

| Function B EC |  ← pushed when B() is called
| Function A EC |
| Global EC     |  ← base context

When B() finishes, it’s popped off → then A() finishes → finally, only the Global EC remains.


5. What are the main components of an Execution Context?

Before diving in, let’s clarify something important:

  • The phases describe how an execution context runs over time (setup → run).

  • The components describe what exists inside that context while it’s running.

Each Execution Context Has Three Core Components

1. Variable Environment (Memory Component)

This is where all your variables and function declarations are stored during the memory creation phase.

It keeps track of identifiers (variable names, function names) and their current values.

  • During creation → variables are set to undefined, and functions are stored completely.

  • During execution → variables get updated with actual values.

Example:

let name = "Alice";
function greet() {}

In memory:

name → undefined (initially)
greet → [Function: greet]

Then in execution:

name → "Alice"

2. Lexical Environment (Scope + Outer Reference)

This defines where a variable or function can be accessed and how JavaScript finds it.

It contains:

  • The current scope (variables inside the current function or block).

  • A reference to the outer (parent) environment for variable lookup.

This creates the scope chain, the reason why inner functions can “see” outer variables.

Example:

function outer() {
  let a = 10;
  function inner() {
    console.log(a); // found via lexical environment
  }
  inner();
}
outer();

Here, inner()’s lexical environment points to outer()’s environment, allowing it to access a.
This mechanism powers closures.


3. this Binding

The special keyword this is also part of every execution context.
It defines what object “owns” the code currently running, and its value depends on how the function was called, not where it’s written.

Example:

const person = {
  name: "Alice",
  greet() {
    console.log(this.name);
  }
};
person.greet(); // "Alice" → this = person

If we extract and call greet() separately:

const greetFn = person.greet;
greetFn(); // undefined → this = global (or undefined in strict mode)

So, this is dynamic, it’s not fixed to where the function is defined, but to how it’s invoked.


6. The Scope Chain & Outer Environment

When JavaScript looks for a variable:

  1. It checks the current execution context.

  2. If not found, it moves outward (to parent contexts) following the scope chain.

  3. If it reaches the global scope and still doesn’t find it → it throws a ReferenceError.

This mechanism makes closures possible, functions “remember” their outer scope even after the parent function has returned.


7. Visualization of Execution Context Flow

|-------------------------------|
| Function C Execution Context  |  <-- top (last called)
|-------------------------------|
| Function B Execution Context  |
|-------------------------------|
| Function A Execution Context  |
|-------------------------------|
| Global Execution Context      |  <-- bottom (first created)

Flow:

  • The Global EC is created first.

  • When A() is called → A’s EC is pushed.

  • When A() calls B() → B’s EC is pushed.

  • If B() calls C() → C’s EC is pushed.

  • Once C() finishes, it’s popped → then B() → then A() → back to global.


8. Full Example, Step-by-Step Execution

let name = "Alice";

function greet() {
  let message = "Hello " + name;
  console.log(message);
}

greet();

Step 1: Global Execution Context Created

name → undefined
greet → function reference stored

Step 2: Memory Creation Phase (GEC)

name = undefined
greet = function

Step 3: Code Execution Phase (GEC)

name = "Alice"
greet() is called → new Function EC created

Step 4: Function Execution Context (FEC)

message = undefined
then message = "Hello Alice"
logs "Hello Alice"

Function EC is popped from the stack.


9. Example 2

let n = 2;
function square( num ){
   let ans = num * num ;
   return ans;
}

let sqaure2 = square(n) // output : 4
let sqaure4 = square(4)  // output : 16

Step-by-Step Execution (With Visual Flow)

global-execution contet flow of code

9. Key Takeaways

  • JavaScript runs inside Execution Contexts (Global + Function).

  • Each context has memory (variables, functions) and code (line-by-line execution).

  • The Call Stack manages which context is currently running.

  • Understanding ECs helps explain hoisting, scope, closures, and this behavior.


In Short

JavaScript builds a mini-universe (Execution Context) every time code runs.
It sets up the memory, runs your logic, manages scope, and cleans up when done.
Mastering this is key to mastering JavaScript itself.

Comments (0)

Join the conversation

Sign in to share your thoughts and engage with other readers.

No comments yet

Be the first to share your thoughts!