Closures are the single most-tested JavaScript concept in technical interviews. They come up at every level, from entry-level screening calls asking "what is a closure?" to senior-level questions about memory leaks, module patterns, and React hook internals. If you can explain closures clearly and debug closure-related bugs on the spot, you signal a genuine understanding of how JavaScript executes, not just how to use it.
This guide covers what closures are, how they work in the JavaScript engine, the classic pitfalls that trip candidates up, and the specific interview questions you are most likely to face.
1. What Is a Closure?
A closure is formed when a function is defined inside another function and retains access to the outer function's variables, even after the outer function has returned and its execution context has been removed from the call stack.
JavaScript uses lexical scoping: a function's scope is determined by where it is written in the source code, not where it is called. When the inner function is created, it holds a reference to the surrounding scope, this reference is the closure.
function makeCounter() {
let count = 0; // 'count' lives in makeCounter's scope
return function increment() {
count++; // increment closes over 'count'
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// makeCounter has returned, but 'count' persists via the closureThe increment function keeps the count variable alive because it holds a reference to it. This is the core mechanism: closures prevent the garbage collector from releasing outer scope variables that inner functions still reference.
2. The Classic Loop Closure Pitfall
The most frequently asked closure question in interviews involves a loop with asynchronous callbacks. This pattern trips up a huge proportion of candidates.
// ❌ Common bug, all callbacks log 3, not 0, 1, 2
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3
// ✅ Fix 1: use let (creates a new binding per iteration)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2
// ✅ Fix 2: IIFE (works with var, pre-ES6 pattern)
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 100);
})(i);
}
// Output: 0, 1, 2i binding for the entire loop. All callbacks close over the same variable. By the time they run, the loop has finished and i === 3. let creates a new binding per iteration, giving each callback its own copy.3. Practical Uses of Closures
Interviewers often follow up "what is a closure?" with "where would you use one?" Here are the four patterns that come up most:
- Data privacy / encapsulation, exposing only a public interface while keeping internal state private (the Module pattern predates ES6 classes).
- Factory functions, functions that produce customized functions (makeMultiplier, makeAdder).
- Partial application and currying, pre-filling some arguments to create a specialized version of a function.
- Memoization, caching expensive computation results in a closed-over Map or object.
// Data privacy via closure
function createBankAccount(initialBalance) {
let balance = initialBalance; // private
return {
deposit(amount) { balance += amount; },
withdraw(amount) { balance = Math.max(0, balance - amount); },
getBalance() { return balance; },
};
}
const account = createBankAccount(100);
account.deposit(50);
console.log(account.getBalance()); // 150
console.log(account.balance); // undefined, not accessible4. Closures and Memory Leaks
Closures keep outer scope variables alive for as long as the inner function exists. In long-running applications (SPAs, Node.js servers), this can cause memory leaks if you unintentionally hold references to large objects.
5. Top Interview Questions on Closures
- What is a closure? Give an example.
- What will this loop print? (The classic var/setTimeout pattern.)
- How is a closure different from a regular function?
- How do closures enable private variables in JavaScript?
- Can closures cause memory leaks? How do you prevent them?
- What is the difference between a closure and a callback?
- How do React hooks use closures internally?
useEffect and useCallback capture variables from their component's render scope, stale closure bugs in React are directly caused by misunderstanding how closures interact with re-renders and dependency arrays.