JavaScript Closures – A Deep Dive (Execution + Memory + Intuition + Real World)

JavaScript Closures – A Deep Dive (Execution + Memory + Intuition + Real World)


Introduction

A closure basically means that a function is bound together with its lexical environment, or a function with its lexical scope is known as a closure. In simple words, function along with lexical scope forms a closure.

Closures are one of the most important and powerful concepts in JavaScript. They are not just theoretical—they are heavily used in real-world applications and even internally by JavaScript engines.

When you truly understand closures, you stop memorizing JavaScript and start understanding it.


What is Actually Happening Behind the Scenes?

Before jumping into examples, let’s build intuition.

👉 Every time JavaScript runs code, it creates an Execution Context. 👉 Every execution context has:

  • Memory (variables + functions)
  • Code (line-by-line execution)

👉 Each function remembers where it was created, not where it is called. 👉 This “remembering” is what we call closure.


Visual Diagram: Execution Context + Scope Chain

1) Call Stack View

Call Stack:

| Global EC |

| x() EC |

| y() EC |


2) Scope Chain Lookup

y() scope:

❌ a not found

x() scope:

✅ a = 7


3) Memory Representation

x() Memory:

a → 7

y → function

y() Closure:

reference → a (from x)


Basic Example

function x(){

var a = 7

function y(){

console.log(a)

}

y()

}

x() // 7

Deep Step-by-Step Execution Flow

Phase 1: Global Execution Context Creation

  • Global EC is created
  • x is stored in memory

Phase 2: Calling x()

  • New Execution Context for x

Memory inside x:

a → 7

y → function

Phase 3: Calling y()

  • New Execution Context for y

Now JavaScript tries to resolve a:

Scope Chain Search

y() → does not have 'a'

x() → has 'a = 7'

So output:

7

👉 The function y() was bound to the variables of function x(). 👉 That is, y() forms a closure and has access to its parent's lexical scope.


Definition

  1. A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).

👉 In other words, a closure gives you access to an outer function's scope from an inner function.

👉 In JavaScript, closures are created every time a function is created, at function creation time.


Closures and Lexical Scoping

Closure in JS is a form of lexical scoping used to preserve variables from the outer scope (x()) of a function in the inner scope of a function (y()).

👉 Lexical = based on code structure 👉 Not based on runtime calling

Think like this:

👉 "Where was the function born?" not "Where is it called?"


Functions Are First-Class Citizens

2) Function Assigned to Variables

function a(){

var x = 9

var b = function y(){

console.log(x)

}

b()

}

a() // 9

👉 Inner function y() forms a closure with x


3) Function Passed as Argument

function x(y){

var a = 9

console.log(y)

y()

}

x(function y(){

console.log("hello world")

})

👉 Function can be passed as argument 👉 Shows functions behave like values


4) Function Returned from Another Function

function x() {

var a = 7

function y(){

console.log(a)

}

return y

}

var z = x();


Important Observation

function x (){

var a = 7

function y(){

console.log(a);

}

return y;

}

x();

var z = x()

console.log(z)

Output:

f y(){ console.log(a) }

👉 Note that here y() was never called 👉 It is just simply being returned by x 👉 So "z" actually contains the whole body of y()

👉 But internally it also carries closure memory


Visual Diagram: Closure Memory

What happens after x() returns?

Before return:

x() EC:

a → 7

y → function

After return:

z → function y()

Closure of y:

a → 7 (still preserved)


Call Stack After Execution

Call Stack:

| Global EC |

(z stored here)

👉 Even though x() is gone, its data survives in closure


Closure in Action

function x (){

var a = 7

function y(){

console.log(a);

}

return y;

}

var z = x()

z(); // 7

Deep Intuition

Even though:

  • x() finished execution
  • Its execution context is removed from call stack

Still: 👉 "z" is able to access a

WHY?

Because: 👉 Functions remember where they come from 👉 They remember variables they had access to 👉 This memory is stored in closure

So when y() was returned:

  • Not just function was returned
  • Its lexical environment reference was also returned

👉 That’s why JS engine can still find a


Updating Values Inside Closure

function x(){

var a = 7

function y(){

console.log(a)

}

a = 100

return y

}

var z = x()

z() // 100

👉 Closures store reference, not value 👉 So updated value is printed


Visual Diagram: Multiple Scope Closure

Lexical Structure:

z()

└── x()

└── y()

Closure(y):

a → 7 (from x)

b → 100 (from z)


Scope Chain Flow

y() → x() → z() → Global


Closures with Multiple Scopes

function z() {

var b= 100;

function x(){

var a=7;

function y() {

console.log(a,b)

}

return y;

}

return x;

}

var p = z()

var t= p()

t() //7,100

Memory Picture

Closure contains:

Closure(y):

a → 7 (from x)

b → 100 (from z)

👉 y() forms a closure along with the scope of x() & z() 👉 That’s why it remembers both values


Another Representation

function z(){

var b= 100;

function x(){

var a=7;

function y() {

console.log(a,b)

}

return y;

}

return x;

}

var p = z()

var t= p()

t() //7,100


Visual Diagram: Real-World Closure

counter() call:

Memory:

count → 0

Returns:

function() { count++ }

Closure:

count → persists across calls

Calls:

c() → count = 1

c() → count = 2

👉 State is preserved using closure


Real-World Understanding

1) Data Privacy

function counter(){

var count = 0

return function(){

count++

console.log(count)

}

}

var c = counter()

c() //1

c() //2

👉 count is private 👉 No one can access it directly 👉 Only closure can access it


2) setTimeout Example

for(var i=1;i<=3;i++){

setTimeout(function(){

console.log(i)

},1000)

}

👉 Output: 4 4 4 👉 Because closure captures same i


Key Takeaways

  • A closure is a function + its lexical environment
  • Created at function creation time
  • Functions remember variables even after execution ends
  • Closures capture references, not copies
  • Scope chain helps resolve variables
  • Prevents garbage collection of needed variables


Uses of Closures

  • Module design pattern
  • Currying
  • Functions like once
  • Memoize
  • Maintain state in async world
  • setTimeouts
  • Iterators
  • And many more

To view or add a comment, sign in

More articles by Babita Pradhan

Others also viewed

Explore content categories