Closures keep outer variables alive after the outer function returns. That powers private state, but the production pitfalls are loop-capture bugs, stale async state, and memory retention when callbacks keep old data alive.
Explain Closures in JavaScript
The Core Idea
A closure means a function remembers variables from the place where it was created, not from where it is called. That is useful for private state and factories, but it also creates common production mistakes: async callbacks reading stale values, loop handlers capturing one shared binding, or long-lived listeners keeping old objects in memory.
// Simple example
function makeCounter() {
let count = 0; // stays in memory
return function () {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// The inner function still 'remembers' count
Why It’s Useful
Closures are powerful because they let you:
- Keep variables private (not accessible globally)
- Store state between function calls
- Create helpers like
once(),memoize(), and other reusable patterns
// Example: once()
function once(fn) {
let called = false;
let value;
return function (...args) {
if (!called) {
called = true;
value = fn.apply(this, args);
}
return value;
};
}
const init = once(() => console.log('Initialized'));
init(); // Logs once
init(); // Does nothing — remembers state
Common Mistake
Using var in loops creates one shared variable for all iterations — so all functions close over the same value:
const funcs = [];
for (var i = 0; i < 3; i++) {
funcs.push(() => i);
}
console.log(funcs.map(fn => fn())); // [3, 3, 3]
Fixes:
- Use
letfor block scoping
- Or use an IIFE (immediately invoked function) to capture a copy
// Fix with let
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0);
}
// 0 1 2
Memory Notes
Closures keep their referenced variables alive as long as the inner function exists. This can accidentally hold big objects or DOM elements in memory, so always clean up unused references.
Imagine you leave a room (outer function ends), but you gave your friend (inner function) a copy of the key to your drawer (variables). Even though you’re gone, they can still open it — that’s a closure!
- Functions automatically form closures in JavaScript.
- Inner functions remember outer variables (their lexical scope).
- Used for data privacy, persistence, and modular code.
- Use
let/constto avoid loop bugs.
- Don’t overuse closures in long-lived objects to prevent memory leaks.
Use the relevant interview-question hub first, then move into a concrete study plan before targeted company sets.