DEV Community

Cover image for Understanding JavaScript Execution Contexts and Lexical Environments
Metin Abbaszade
Metin Abbaszade

Posted on

Understanding JavaScript Execution Contexts and Lexical Environments

Whenever JavaScript runs code, the engine needs a workspace to manage what’s happening. This workspace answers key questions:

  1. Which variables and functions are available? → Scope
  2. Where should I look if a variable isn’t here? → Scope Chain
  3. What is this right now? → Context Object

This workspace is called an Execution Context, and every execution context contains three main components:

*Lexical Environment (LE) *→ Stores variables, functions, parameters, and references to outer environments.

Variable Environment (VE) → Internal storage mainly for var declarations and function declarations.

this binding → Determines the current context object.

Types of Execution Contexts

  1. Global Execution Context (GEC) → Created first when JS starts. Holds the Global Lexical Environment. Only one GEC exists.

  2. Function Execution Context (FEC) → Each function call creates a new Execution Context with its own Lexical Environment (local variables, arguments, inner functions). Its Outer points to the parent scope (where the function was defined).

  3. Eval Execution Context (EEC) → Created when using eval("..."). Runs code from a string. ⚠️ Rarely used and considered bad practice (security + performance issues).

Example 1 – Global + Function Execution Context

// Global Execution Context
var name = "Metin";

function sayHello() {
  // Function Execution Context
  var greeting = "Hello";
  console.log(greeting + ", " + name);
}

sayHello();
Enter fullscreen mode Exit fullscreen mode

Now lets go analyze Step-by-Step:

  1. Global Execution Context (GEC) created:
Lexical Environment: { name: "Metin", sayHello: <function> }, Outer: null

Scope: name, sayHello

Scope Chain: Only global

this: window (browser) or global (Node.js)
Enter fullscreen mode Exit fullscreen mode
  1. Function Execution Context (FEC for sayHello) created:
Lexical Environment: { greeting: "Hello" }, Outer → Global Lexical Environment

Scope: greeting

Scope Chain: Function scope → Global scope

this: undefined (strict mode)
Enter fullscreen mode Exit fullscreen mode

Variable lookup:

greeting → found in FEC

name → not in FEC → check outer → Global LE → found "Metin"
Enter fullscreen mode Exit fullscreen mode

Output:

Hello, Metin
Enter fullscreen mode Exit fullscreen mode

Example 2 – Lexical Environment and Closures

function outer() {
  let counter = 0;  // outer lexical environment

  function inner() {
    counter++;  // inner still "remembers" outer's variables
    console.log(counter);
  }

  return inner;
}

const fn = outer();  
fn(); // 1
fn(); // 2
fn(); // 3
Enter fullscreen mode Exit fullscreen mode

Step-by-Step

  1. Call outer() → FEC for outer created:
Lexical Environment: { counter: 0, inner: <function> }, Outer → Global LE

Scope: counter, inner

Scope Chain: outer → global

this: undefined (strict mode)

inner returned → closure formed → retains reference to outer LE
Enter fullscreen mode Exit fullscreen mode
  1. Call fn() → FEC for inner created:
Lexical Environment: {}, Outer → Lexical Environment of outer()

Scope: empty

Scope Chain: inner → outer → global

this: undefined
Enter fullscreen mode Exit fullscreen mode

Variable lookup:

counter → not in inner → outer LE → found → incremented
Enter fullscreen mode Exit fullscreen mode

Output sequence:

1
2
3
Enter fullscreen mode Exit fullscreen mode

Top comments (0)