JavaScript Closures
Learn how inner functions remember variables from their parent scope forever, keep private data safe, and avoid common loop timer traps.
1. What is a Closure?
A closure happens when an inner function remembers and has access to variables from its parent function, even after the parent function has finished running and closed.
💡 Think of a Function Carrying a Travel Backpack
When an inner function is born inside a parent function, it packs all the parent's variables into its magical backpack. Wherever that inner function travels later in your code, it still carries and can use everything inside that backpack!
function createGreeting(greetingWord) {
// greetingWord is in the parent scope
return function(personName) {
// The inner function remembers 'greetingWord' forever!
return `${greetingWord}, ${personName}!`;
};
}
const sayHello = createGreeting("Hello");
const sayNamaste = createGreeting("Namaste");
// createGreeting() is already finished, but sayHello still remembers "Hello":
console.log(sayHello("Alex")); // "Hello, Alex!"
console.log(sayNamaste("Priya")); // "Namaste, Priya!"
Hello, Alex! Namaste, Priya!
2. Keeping Data Private (Encapsulation)
In JavaScript, variables inside a function are protected. By returning methods that interact with them, you can create private counters and secure state:
function createCounter() {
let count = 0; // 🔒 Private variable trapped inside the closure!
return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
},
getCount() {
return count;
}
};
}
const counterA = createCounter();
const counterB = createCounter(); // Has its own separate private count!
console.log(counterA.increment()); // 1
console.log(counterA.increment()); // 2
console.log(counterB.increment()); // 1 (Isolated from counterA!)
// count cannot be modified directly from outside:
// console.log(counterA.count); // undefined (Safe!)
1 2 1
3. The Classic Loop + Timer Closure Trap
This is one of the most famous JavaScript interview questions in tech:
The `var` Shared Scope Trap ❌
var shares a single variable across all loops. By the time the timers run, i is already 3.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 50);
}
// Outputs: 3, 3, 3
The Modern `let` Solution ✅
let creates a fresh new block scope on every single iteration, saving the exact current value into its closure.
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 50);
}
// Outputs: 0, 1, 2
4. Real-World Use Case: Function Caching (Memoization)
Closures let you remember previous results so you never have to re-run expensive calculations twice:
function createMultiplier(multiplier) {
// Remembers 'multiplier' in its closure:
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(10)); // 20
console.log(triple(10)); // 30
20 30
5. Quick Closure Summary
| Concept | How it Works | Main Benefit |
|---|---|---|
| Lexical Memory | Inner functions preserve outer variables | Data persists without global variables |
| Data Privacy | Variables stay locked inside the outer function | Prevents accidental external modification |
| Function Factories | Functions that generate custom functions | Reusable, configurable logic (e.g. double, triple) |
6. Closures Interview Deck
Test your knowledge on closure persistence, multiple instances, loop bindings, function factories, and variable isolation:
7. Practice Challenge
🎯 Coding Challenge: Secure Bank Account Factory
Build a createBankAccount(initialBalance) function factory that encapsulates a private balance variable. Provide deposit(amount), withdraw(amount), and getBalance() methods that manipulate the balance securely.