DEV Community

Cover image for Closure in JavaScript
Sagar Kumar Shrivastava
Sagar Kumar Shrivastava

Posted on

Closure in JavaScript

What is Closure?

To truly understand closures in JavaScript, we first need to understand how JavaScript executes code under the hood.

Closures are not a separate feature, they are a natural result of how Execution Contexts and Lexical Environments work.

Let's break this down step by step.

Execution Context

Whenever a function is invoked, JavaScript creates a new Execution Context for that function.

An Execution Contexts fundamentally consists of two main components:

  1. Variable Environment (Memory Component)
  2. Lexical Environment (Scope Resolution Component)

1. Variable Environment (Memory Component)

This is where the function's local data is stored.

Before a single line of code is executed, the JavaScript engine scans the function and allocates memory for:

  • Variables declared using var, let, and const
  • Function declarations (entire function definitions)
  • Arguments object, which contains all values passed to the function

This setup happens during the Creation Phase of the Execution Context.

2. Lexical Environment (Scope Resolution Component)

This component is responsible for executing the code line by line and resolving variables.

It contains:

  • Scope Chain
    A reference to the parent's Lexical Environment. This is how JavaScript looks up variables that are not present in the current scope.

  • this binding
    The value of this, determined by how the function is called.

The Two Phases of Execution Context

Every Execution Context is created in two phases:

1️⃣ Creation Phase

  • Memory is allocated
  • Variables are hoisted
  • Functions are stored entirely
  • let and const exist in the Temporal Dead Zone

2️⃣ Execution Phase

  • Code runs line by line
  • Variables receive their actual values

Let's Understand This with an Example

Code for Closure

Step 1: Global Execution Context (GEC)

Before any code runs, JavaScript creates the Global Execution Context.

Memory Phase

  • outer → stored as a function
  • fn → initialized as undefined

Execution Phase

  • The engine reaches const fn = outer()
  • This triggers the creation of a new Execution Context for outer()

Global Execution Context (GEC)

Step 2: outer() Execution Context

When outer() is called, its Execution Context is pushed onto the Call Stack.

Memory Phase

  • count is allocated (in TDZ because it’s declared with let)
  • inner function is created and stored entirely

Execution Phase

  • count is assigned the value 0
  • The inner function is returned

Termination

  • The outer() Execution Context is popped off the Call Stack

Outer Execution Context

⚠️ Crucial Detail (This is the key to Closure)
Even though the outer() Execution Context is destroyed, its Lexical Environment is NOT garbage collected.

Why?

Because the returned function inner still holds a reference to it.

👉 This preserved reference is what we call a Closure.

Step 3: fn() Execution Context (First Call)

At this point:

const fn = outer();

fn now holds a reference to the inner function along with its hidden [[Environment]] link to outer’s Lexical Environment.

When we call:

fn();

Memory Phase

  • No local variables
  • Empty arguments object

Execution Phase

  • count++ is executed
  • JavaScript looks for count in fn’s local scope → not found
  • It follows the Scope Chain to outer’s Lexical Environment
  • count is found with value 0
  • It is incremented to 1
  • 1 is returned

Fn Execution Context

So, What is a Closure?

A Closure is formed when a function "remembers" the variables from its lexical scope even after that scope's execution context has finished.

Closures happen because:

  • Functions carry a hidden reference ([[Environment]])
  • This reference points to the Lexical Environment where the function was created
  • As long as the function exists, that environment stays alive

Mental Model 🧠

🔑 Closure is not about function execution, it's about function creation.
A function remembers the lexical environment where it was defined, even after that environment's execution context is gone.

Top comments (0)