What it does: When a function is created inside another function, it “closes over” (captures) variables from the outer scope. Even after the outer function finishes, the inner function remembers those variables.
What Actually Happens - The Behavior
Let’s say you have this:
javascript
function parent() {
let a = 10;
return function inner() {
console.log(a);
}
}
let closure = parent();
closure(); // prints 10What you see: The inner function can still access a even though parent() finished running.
Reference vs Copy - The Key Point
function parent() {
let a = 10;
return function inner() {
a++;
console.log(a);
}
}
let closure = parent();
closure(); // prints 11
closure(); // prints 12Behavior: The variable a keeps changing! This proves it’s a reference, not a copy.
cause say if it took a as literal copy with it and you called closure() , time it would invoke a=10
because its refence you are modifying same memory space this incrementing value
function counter() {
let count = 0;
return function() {
count++; // Modifies the SAME count variable
return count;
};
}
const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2 (same count variable!)
console.log(increment()); // 3What happens in memory:
counter()createscountat memory address 0x100- Inner function captures reference to 0x100 (not the value)
counter()finishes, removed from call stack- But
countat 0x100 stays alive because inner function references it - Each
increment()call modifies the same memory location
Why Don’t Variables Get Destroyed?
You’re right about garbage collection! Here’s what happens:
- Normal case: When
parent()finishes, its variables would be garbage collected - Closure case: The returned function still has a reference to
a - Garbage collector logic: “Someone still needs
a, so I won’t delete it” - Result: Variable
astays alive in memory
The garbage collector uses “mark and sweep” - it only removes things with no references. Since the closure references a, it stays alive.
It’s Not Just About Function Definitions
The closure isn’t just the function definition - it’s the function + its captured environment. When you call closure(), it actually executes and uses those referenced variables.
All Forms of Closures
Closures happen whenever a function accesses variables from outside its own scope:
- Returned functions (your example)
- Callbacks:
setTimeout(function() { console.log(a); }, 1000) - Event handlers:
button.onclick = function() { console.log(a); } - Functions stored in variables or objects
You can’t return methods from objects in the same way because objects don’t create new scopes like functions do.
1. Returned Functions (Most Common)
function factory() {
let x = 1;
return function() { return x; };
}2. Callback Functions
function setup() {
let config = "settings";
setTimeout(function() {
console.log(config); // Closure over config
}, 1000);
}3. Event Handlers
function addHandler() {
let clickCount = 0;
button.onclick = function() {
clickCount++; // Closure over clickCount
console.log(clickCount);
};
}4. IIFE with Closures
const module = (function() {
let private = "secret";
return {
getPrivate() {
return private; // Closure over private
}
};
})();5. Loop Variables (let creates closures)
javascript
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // Each callback closes over different 'i'
}, 100);
}Interview Gotchas:
Gotcha 1: Shared vs Individual Closures
javascript
function createFunctions() {
let functions = [];
for (var i = 0; i < 3; i++) {
functions.push(function() {
return i; // All closures share same 'i'
});
}
return functions;
}
const fns = createFunctions();
console.log(fns[0]()); // 3 (not 0!)
console.log(fns[1]()); // 3 (not 1!)
console.log(fns[2]()); // 3 (not 2!)Gotcha 2: Closure in Object Methods
javascript
function createObject() {
let secret = "hidden";
return {
getSecret() {
return secret; // Method has closure over secret
}
};
}Gotcha 3: Multiple Closures, Same Variable
javascript
function factory() {
let shared = 0;
return {
increment() { shared++; },
decrement() { shared--; },
getValue() { return shared; }
};
}
const obj = factory();
obj.increment(); // shared = 1
obj.increment(); // shared = 2
console.log(obj.getValue()); // 2Gotcha 4: Closure Memory Leaks
javascript
function problematic() {
let bigData = new Array(1000000).fill("data");
return function() {
console.log("hi"); // Doesn't use bigData but keeps it alive!
};
}Mental Model - The Snapshot:
Think of closure as taking a “live photo”:
- When inner function is created, it takes a snapshot of what variables it needs
- The snapshot contains addresses, not values
- Even after outer function is gone, the snapshot keeps those memory addresses alive
- Multiple closures can share the same addresses
Why Only Functions Create Closures:
Objects/Classes don’t create closures because:
- They don’t have lexical scope
- Properties are accessed via
this, not scope chain - Methods are just functions (which can have closures)
javascript
const obj = {
method() {
// This is just a function, can have closures like any function
}
};Bottom line: Closures happen when inner functions capture references to outer scope variables. The captured variables stay alive in memory as long as the closure function is reachable. Think “live reference snapshot” - not copying values, but keeping memory addresses alive through garbage collection.
