What Is a Closure?
A closure is a function that retains access to variables from its outer (enclosing) scope, even after the outer function has finished executing. This is one of the most powerful features in JavaScript.
Basic Closure Example
function createCounter() {
let count = 0;
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
Practical Example: Function Factory
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Practical Example: Memoization
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) return cache[key];
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
}
const expensiveCalc = memoize((n) => {
console.log('Computing...');
return n * n;
});
expensiveCalc(4); // Computing... 16
expensiveCalc(4); // From cache 16
The Classic Loop Problem
// PROBLEM: All buttons log 5
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}
// Output: 5, 5, 5, 5, 5
// SOLUTION with closure
for (var i = 0; i < 5; i++) {
((index) => {
setTimeout(() => console.log(index), 1000);
})(i);
}
// Output: 0, 1, 2, 3, 4
Closures in React Hooks
function TodoApp() {
const [todos, setTodos] = useState([]);
const addTodo = useCallback((text) => {
setTodos(prev => [...prev, { id: Date.now(), text, done: false }]);
}, []);
return (
<div>
<button onClick={() => addTodo('New task')}>Add</button>
</div>
);
}
Module Pattern with Closures
const BankAccount = (function() {
let balance = 0;
const transactions = [];
function addTransaction(type, amount) {
transactions.push({ type, amount, date: new Date() });
}
return {
deposit(amount) { balance += amount; addTransaction('deposit', amount); return balance; },
withdraw(amount) { balance -= amount; addTransaction('withdrawal', amount); return balance; },
getBalance() { return balance; },
getTransactions() { return [...transactions]; }
};
})();
BankAccount.deposit(1000);
BankAccount.withdraw(200);
console.log(BankAccount.getBalance()); // 800