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:
👉 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
Phase 2: Calling x()
Memory inside x:
a → 7
y → function
Phase 3: Calling 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
👉 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:
Recommended by LinkedIn
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:
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:
👉 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
Uses of Closures