Closures are the most frequently tested JavaScript concept. Master them with real interview questions, answers, and code examples.
When you create a function inside another function, JavaScript gives the inner function a backpack. It automatically packs up every variable it can see at the moment of creation — and carries that backpack everywhere it goes, forever. Even after the outer function has finished and is gone from memory, the inner function still has its backpack. Still has those variables. That's a closure. The key insight: functions remember where they were born, not where they run.
JavaScript uses lexical scoping — a function's scope is determined by where it's written in the code, not where it's called from. Closures are the natural consequence of this combined with first-class functions.
function outer() {
const message = 'I am in the backpack'
return function inner() {
console.log(message) // works even after outer() is gone
}
}
const fn = outer() // outer() runs and returns — 'message' should be gone…
fn() // 'I am in the backpack' — but it's not. Closure.
The most common real pattern. count is completely private — no way to set it or read it from outside except through the methods you expose.
function createCounter() {
let count = 0 // private — nobody outside can touch this
return {
increment() { count++ },
decrement() { count-- },
value() { return count }
}
}
const counter = createCounter()
counter.increment()
counter.increment()
counter.decrement()
console.log(counter.value()) // 1
console.log(count) // ReferenceError — count doesn't exist out here
Each call to multiplier() creates a new closure with a different factor in its backpack. They're independent — changing one doesn't affect the others.
function multiplier(factor) {
return function(number) {
return number * factor // 'factor' is in the backpack
}
}
const double = multiplier(2)
const triple = multiplier(3)
console.log(double(5)) // 10
console.log(triple(5)) // 15
This trips up experienced developers. Every interviewer uses it.
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i)
}, 1000)
}
// Most people expect: 0, 1, 2
// What actually prints: 3, 3, 3
Why: var is function-scoped — there's only one i variable for the whole loop. All three closures point to the same i. By the time setTimeout fires, the loop is done and i is 3.
// Fix 1 — use let (creates a new binding per iteration)
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i) // 0, 1, 2 ✓
}, 1000)
}
// Fix 2 — IIFE (manually create a new scope)
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j) // 0, 1, 2 ✓
}, 1000)
})(i)
}
A junior answer quotes the definition: "A closure is when a function has access to its outer scope's variables even after the outer function has returned." Technically correct. Not memorable.
A senior answer explains the backpack analogy, demonstrates the loop problem without being asked, and mentions React hooks or the module pattern as real-world usage. That's what gets you hired.
Closures copy the variable value" — Wrong. They hold a reference to the variable. If the variable changes after the closure is created, the closure sees the new value. This is exactly why the var loop problem happens — all closures share the same reference to i.
Closures only work when you use return" — Wrong. Any function that accesses an outer variable is a closure — callbacks, event handlers, methods. The backpack forms at creation time, not at return.
Closures cause memory leaks" — They can, if you close over large objects you don't need and never release the reference. But closures themselves are not leaks. Used correctly they're completely fine and used everywhere.
React's useState hook — the updater function closes over the current state value. This is why stale closures happen in useEffect: the callback captures the value of state at render time, not the latest value. Fix with the functional updater form: setCount(prev => prev + 1).
Event handlers — addEventListener callbacks always form closures. When you write btn.addEventListener('click', () => console.log(label)), the callback closes over label from the outer scope and remembers it forever, even after the setup function returns.
The module pattern — before ES modules existed, developers used IIFEs to create private state. The returned object has access to private variables through closure, but outside code can't touch them directly. Still used in many libraries today.
Memoization / caching — a memoize function closes over a cache object. Every call to the returned function can read and write to that same cache, persisting results between calls without any global variables.
Explain closures with a practical example.
What is the difference between call, apply, and bind?
Classic var in loop closure
Stale closure in React useEffect
Memoization closure caching wrong scope
let in loop closure (fix)
Closure counter
IIFE closure
Closure variable lookup
Shared closure mutation
bind creates new function
Reading answers is not the same as knowing them. Practice saying them out loud with AI feedback — that's what builds real interview confidence.