Ask anything about this article
Hi! I've read this article.
What would you like to know?
@farhan
Closures are one of the most important concepts in JavaScript and one of the most commonly asked topics in interviews. If you understand closures, you understand JavaScript. Let's demystify them.
A closure is a function that remembers the variables from its outer scope even after the outer function has finished executing.
javascriptfunction createCounter() {
let count = 0; // This variable is "enclosed" in the closure
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// 'count' persists between calls!
When createCounter() finishes executing, normally count would be garbage collected. But because the inner function still references count, JavaScript keeps it alive. The inner function "closes over" the variable.
javascriptfunction createBankAccount(initialBalance) {
let balance = initialBalance; // Private variable
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount > balance) throw new Error('Insufficient funds');
balance -= amount;
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(1000);
account.deposit(500); // 1500
account.withdraw(200); // 1300
console.log(account.balance); // undefined! It's private
javascriptfunction 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
javascriptfunction debounce(fn, delay) {
let timeoutId; // Closure variable
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
const debouncedSearch = debounce((query) => {
console.log('Searching:', query);
}, 300);
javascriptfunction memoize(fn) {
const cache = {}; // Closure variable
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) return cache[key];
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
}
const expensiveCalculation = memoize((n) => {
console.log('Computing...');
return n * n;
});
expensiveCalculation(5); // Computing... 25
expensiveCalculation(5); // 25 (from cache, no "Computing..." log)
javascript// ❌ Bug: All callbacks print 5
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}
// Output: 5, 5, 5, 5, 5
// ✅ Fix with closure (IIFE)
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(() => console.log(j), 1000);
})(i);
}
// Output: 0, 1, 2, 3, 4
// ✅ Modern fix: use let
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}
// Output: 0, 1, 2, 3, 4
Closures keep variables alive, which means they consume memory. Be careful with closures in loops or event listeners — they can cause memory leaks if not properly cleaned up.
javascript// ❌ Memory leak: closure keeps large data alive
function processData() {
const hugeArray = new Array(1000000).fill('data');
return function() {
// Even if we don't use hugeArray, it stays in memory
return 'done';
};
}
// ✅ Fix: null out large references
function processData() {
let hugeArray = new Array(1000000).fill('data');
const result = hugeArray.length;
hugeArray = null; // Allow garbage collection
return function() {
return result;
};
}
Once closures click, you'll see them everywhere in JavaScript — in React hooks, Express middleware, event handlers, and beyond.