🎒
Closures
intermediateA closure is a function that remembers the variables from its outer scope even after the outer function has returned. Think of it as a function carrying a "backpack" of variables wherever it goes.
🎮Interactive Visualization
CodeCreation Phase
1function outer() {2 let x = 10;3 function inner() {4 return x;5 }6 return inner;7}89const fn = outer();10fn(); // 10
📚Call Stack
Global EC
Variables:
fnundefined
🗄️Heap Memory
(empty)
📤Output
—
Step 1/6Global EC created - outer function is defined
Key Insight: A closure is formed when an inner function is returned from an outer function, maintaining access to the outer function's variables even after the outer function has completed.
Key Points
- A closure is created every time a function is created
- Inner functions have access to outer function variables
- The outer variables are captured by reference, not by value
- Closures enable data privacy and stateful functions
💻Code Examples
Basic Closure
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
const counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3Inner function remembers outer variables
Counter Example
function createCounter(start) {
let count = start;
return {
increment: () => ++count,
decrement: () => --count,
getValue: () => count
};
}
const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.getValue(); // 12Each call uses the same closed-over variable
Private Variables
function createAccount(initial) {
let balance = initial; // private!
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount > balance) {
return "Insufficient funds";
}
balance -= amount;
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createAccount(100);
account.deposit(50); // 150
account.balance; // undefined (private!)Closures enable data privacy
Multiple Closures
function createCounter(start) {
let count = start;
return () => count++;
}
const counterA = createCounter(0);
const counterB = createCounter(100);
counterA(); // 0
counterA(); // 1
counterB(); // 100
counterB(); // 101
// Each closure has independent stateEach closure has its own independent state
Loop Closure Bug
// THE BUG: All callbacks share same i
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
// Output: 3, 3, 3
// Why? var is function-scoped
// All 3 closures reference the
// SAME i, which is 3 after loopClassic var loop closure problem
Loop Fix with let
// THE FIX: let creates new binding
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
// Output: 0, 1, 2
// Why? let is block-scoped
// Each iteration gets its own i
// Each closure captures different ilet creates new binding per iteration
Common Mistakes
- Loop variable capture - all callbacks share the same variable
- Memory leaks from closures holding large objects
- Forgetting that closures capture by reference, not value
Interview Tips
- Use the "backpack" metaphor to explain closures
- Show practical uses: data privacy, function factories, memoization
- Be ready to solve the classic loop closure problem