What is closure? Where have you used it
Closure = function + the variables of its enclosing scope, kept alive because the function references them. Real uses: private state in factories/modules, memoization caches, partial application/currying, React hooks capturing renders' props, debounce/throttle holding their timer, event handlers needing per-instance config.
See [[closures-and-lexical-scoping]] for the deeper mechanics. This question is usually a "tell me a real example" softball.
Definition
A function that retains access to variables from the scope where it was defined, even after that scope has returned.
Real-world examples
Private state in a factory
function createCounter() {
let n = 0;
return {
inc: () => ++n,
get: () => n,
};
}n is private; only the returned methods can touch it. Module pattern in a nutshell.
Memoization
function memoize(fn) {
const cache = new Map();
return (key) => {
if (!cache.has(key)) cache.set(key, fn(key));
return cache.get(key);
};
}cache lives across calls — held alive by the closure.
Debounce / throttle
function debounce(fn, ms) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}t survives between calls because the returned function captures it.
Partial application
const add = (a) => (b) => a + b;
const add5 = add(5);
add5(2); // 7The inner (b) => a + b closes over a.
React hooks
Every render is a new closure. useState, useEffect, useCallback work by capturing the current render's values:
const [count, setCount] = useState(0);
useEffect(() => { console.log(count); }, [count]);
// Each effect callback captures THAT render's count.If you write useEffect(() => { setInterval(() => log(count), 1000); }, []) with empty deps, the interval closure captures the initial count — the classic stale-closure bug.
Event handlers with per-row state
items.map((item) => <button onClick={() => onSelect(item.id)}>...</button>)The onClick closes over item.id for that row.
Configuration baked into a factory
function makeLogger(prefix) {
return (msg) => console.log(`[${prefix}]`, msg);
}
const error = makeLogger("ERROR");
error("boom");What I'd say in an interview
Pick one example and tell a story:
"I used closures recently to build a small toast queue. The factory returns{ enqueue, subscribe }; the queue array and listener set are captured in the closure, so nothing outside the module can mutate them — encapsulation without a class. The subscribe function returns anunsubscribethat closes over the listener reference. It's how Zustand, Redux, and event emitters work under the hood."
Or React-specific:
"Every time I write useEffect, I'm relying on closures — the effect callback captures that render's state and props. Forgetting that gives you stale-closure bugs where an interval logs an old value. The fix is either listing the value in deps or reading from a ref."
Interview framing
"A closure is a function plus its enclosing scope's variables. Practical uses: private state in factories (module pattern, Zustand stores), memoization caches, debounce/throttle holding timers, partial application, React hooks capturing per-render values, event handlers carrying per-item context. The classic gotcha is stale closures — useEffect with empty deps logging the initial value forever. Pick one real example and tell the story rather than reciting the definition."
Follow-up questions
- •Walk through a stale closure bug.
- •Closures vs classes for private state — when which?
- •How do closures cause memory leaks?
Common mistakes
- •Reciting the definition without an example.
- •Using var in loops + closures (classic interview pitfall).
- •Stale closures from missing useEffect deps.
Performance considerations
- •Captured variables stay alive; large objects in long-lived closures can leak.
Edge cases
- •Closures over loop variables (let vs var).
- •Holding refs to large objects in long-lived closures.
Real-world examples
- •Toast queues, throttle/debounce, React hooks, Zustand stores, event emitters, factories.