Free lesson · JavaScript Basic to Advance
Interview Lens: Closures
The Question
"Can you explain what a closure is, and give me an example of when you'd use one?"
That's the question. Three variations interviewers cycle through:
"Why does this code log this value?" — followed by code with closures and asynchronous behavior, usually involving
setTimeoutorsetInterval."Write a function that creates a private counter."
"What's the difference between scope and closure?"
This is the most-asked deep JavaScript question in interviews. It's not optional. Every JavaScript-focused interview will probe closures in some form. Get this right and the rest of the interview feels easier. Get this wrong and the interviewer's confidence in your fundamentals drops, which makes every subsequent question harder.
Arun on the interview psychology and the recovery strategies. Nikhil on the technical depth — when an interviewer pushes past "what is a closure" into execution-context territory, this is the section to read carefully.
Why They Ask It
Closures are the cleanest test of JavaScript runtime model understanding. A candidate who really understands closures has internalized scope, function-as-values, the execution model, and the fact that JavaScript variables can outlive the functions that declared them. All of that bundled into one concept.
Three signals interviewers are listening for:
Signal 1: Do you have the precise definition or just a vibe? "A closure is a function inside a function" is wrong — that's nested functions, not closures. The correct definition involves the function plus its remembered lexical scope. Candidates who give the vague answer signal they've heard the word without studying the concept.
Signal 2: Can you trace through closure behavior step by step? When given code, do you reason carefully about which scope each variable comes from, or do you guess based on pattern recognition? Tracing is the senior skill. Guessing produces wrong answers under pressure.
Signal 3: Do you connect closures to practical uses? Memoization, private state, partial application, event handlers, async callbacks — these are all closure-powered patterns. A candidate who can name two or three real uses signals practical fluency. A candidate who can only define closures in the abstract signals book learning.
When I (Arun) bombed my third interview, the closure question was where it happened. I gave the definition correctly. Then the interviewer wrote some code and asked me to predict the output. I didn't trace carefully — I just looked at it and said what I thought the answer was. Wrong. He let me run it. We watched the wrong answer print. I had to explain why, and at that point I was rebuilding my understanding in front of him instead of demonstrating it. Bad signal.
The Wrong Answers
Warning: The closure mistakes interviewers hear most often.
"A closure is a function inside a function." This is incomplete. Nested functions exist in many languages without producing closures. The correct definition includes the function's access to its enclosing lexical scope, even after the enclosing function has returned. Without that second half, you've described syntax, not the concept.
"A closure is when a variable is private." This confuses outcome with mechanism. Privacy is one use of closures, not what closures are. Saying "closures are for data hiding" is like saying "loops are for printing things" — true in some cases, but not the definition.
"Closures are when functions share variables." Partially right. They do share variables. But the precise claim is that a function "remembers" the scope it was defined in, which lets it access those variables when called later. The sharing is a consequence of multiple functions being defined in the same scope.
"
letandconstdon't create closures, onlyvardoes." No — closures work with any variable declaration. The difference betweenvarandletis scoping (function vs block), which affects what the closure captures, not whether a closure is created.
The Right Answer
Three layers depending on how much depth the interviewer wants.
The 30-Second Answer
"A closure is a function that retains access to variables from the scope in which it was defined, even after that scope has finished executing. Whenever you define a function inside another function and return or otherwise persist the inner function, you've created a closure. The inner function carries its lexical scope with it."
That's enough for a screen or a warm-up. Stop here unless they push.
The 2-Minute Answer
Build on the above:
"The mechanism is that JavaScript uses lexical scope — functions remember the scope they were written in, not the scope they're called from. Every function holds a reference to its enclosing environment. Normally, when a function returns, its execution context is destroyed and its variables become eligible for garbage collection. But if an inner function still exists somewhere — stored in a variable, passed to a callback, returned to the caller — JavaScript can't garbage-collect the outer function's variables, because the inner function might still need them. That preserved access is the closure.
In practice, I use closures for several patterns. Private state with the module pattern — a factory function returns an object with methods that all close over private variables. Memoization — a wrapper function holds a cache in closure and uses it to return cached results. Partial application — a function takes some arguments and returns a customized function that closes over them. Event handlers and callbacks — almost any function passed as an argument that references outer variables is a closure.
The famous closure gotcha is the for loop with var. Because var is function-scoped, all iterations share the same variable. If you set up asynchronous callbacks in a loop and they close over the loop variable, they all see the final value, not their iteration-specific value. The fix is let, which creates a new variable per iteration with block scope."
This is the answer for a standard onsite when they want real depth.
The Deep Dive (Nikhil's territory)
For senior or staff interviews where the interviewer probes the runtime model:
"At the engine level, closures work because of how execution contexts are constructed. When a function is defined, the engine attaches a reference to the variable environment of the surrounding execution context. This reference is part of the function's internal state, accessible at all times.
When the surrounding context's execution finishes — when the outer function returns — the context itself is popped off the call stack, but the variable environment it created can stay alive in memory. The garbage collector won't reclaim it as long as anything holds a reference to it. The returned inner function is exactly such a reference. So the variable environment persists, and the inner function can continue accessing it.
This is why we can talk about closures as 'capturing scope.' The inner function isn't capturing values — it's capturing a reference to the entire environment object, with all its bindings. If the bindings change after capture, the closure sees the new values. This is also why mutation-based bugs are so common with closures: the captured variable isn't frozen in time.
Modern engines do optimize this. V8, for example, can sometimes detect that a closure only references certain variables and free the rest of the environment. But you can't rely on that optimization at the language level — the spec only guarantees that referenced variables stay alive.
The connection to async is critical. When you pass a callback to setTimeout, the callback closes over the surrounding scope. When the callback eventually fires — milliseconds, seconds, or hours later — it still sees its closure environment, which might have been mutated since the callback was created. This is the source of countless React stale-closure bugs and similar issues across frameworks."
This level signals real depth. Use it when you're being probed at senior or staff levels.
Interactive animation: Kept by reachability - the finished call's variables stay in memory while the returned function still points at them.
Walking Through the for Loop Question
This is the single most-asked closure puzzle. Walk through it carefully:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
Verbal walkthrough:
"The loop uses var, so i is function-scoped — there's only one i for the entire loop. The loop runs to completion synchronously, setting up three setTimeout callbacks. By the time those callbacks fire 100 milliseconds later, the loop has finished and i is 3. All three callbacks read the same i, which is now 3. So we get 3, 3, 3."
Then:
"The fix is to change var to let. With let, each iteration creates a new i in its own block scope. Each setTimeout callback closes over its iteration-specific i. The three callbacks log 0, 1, 2."
If you can walk through this without hesitation, you've signaled mastery. If you can also mention the pre-let workaround using IIFEs, that's a bonus that signals JavaScript history awareness.
Interactive animation: The three-question procedure, applied - var, let and the IIFE workaround, each traced to what it prints.
Implement a Counter: The Classic Challenge
"Write a function that creates a counter" is the standard closure implementation question.
function createCounter(start = 0) {
let count = start;
return {
increment: () => ++count,
decrement: () => --count,
reset: () => { count = start; return count; },
getValue: () => count
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getValue()); // 2
counter.reset();
console.log(counter.getValue()); // 0
Verbal narration as you write:
"I'll define a factory function. Inside, I declare count with let — this is the private state. I return an object with methods that all close over count. Each call to createCounter produces a fresh closure, so each counter has its own private count. There's no way to read or modify count from outside the returned object."
The interviewer might extend the question: "Add a maximum value. Throw an error if increment would exceed it." That's another closure variable (maxValue) captured by the methods. Same pattern, more closure work.
Interactive animation: Sentence first, then the proof - and a second factory call starts its own fresh count.
The Follow-Ups
Interview: What the interviewer asks next once you've answered well.
"Why does the
forloop withvarlog the wrong values?" Walk through scope (varis function-scoped) and closures (callbacks capture references, not values). The fix islet."What's the difference between scope and closure?" Scope is the rule about where variables can be accessed. Closure is the thing that exists at runtime when a function carries its lexical scope with it.
"How would you implement a private variable in JavaScript?" Factory function with a returned object whose methods close over the private variable. Show an example. Mention that modern classes also have
#privateFieldsyntax."Can closures cause memory leaks?" Yes. Captured variables stay alive as long as the closure does. Long-lived closures on persistent objects (event listeners on the DOM, callbacks in registries) can accumulate memory. Mention LRU eviction for caches.
"What's a real production use of closures you've written?" Have one ready. Memoization for an expensive selector. Event handler with captured context. Custom validation that closed over rule configuration. The interviewer wants to see you've actually used closures, not just memorized them.
The Founder Moment
Nikhil here. The closure question came up in my third interview at Google. The interviewer wrote out the
forloop withvarand asked me what it would print. I answered correctly — three threes. Then he asked why. I explainedvar's function scoping and the closure capturing the shared reference. He nodded.Then he asked the follow-up I wasn't ready for: "Walk me through what's actually happening at the execution context level." I started talking about scope chains and lexical environments, but I got tangled in terminology. I knew the concept — I just didn't have the vocabulary on the tip of my tongue. The interview moved on, but the moment cost me confidence for the rest of the round.
I didn't get the offer. The system design round was where I actually failed, but I was off-balance from the closure moment.
What I took from that: knowing closures is not enough. You have to be able to talk about closures in multiple registers — the casual definition for a quick question, the practical examples for a discussion, and the engine-level mechanism for the deep probe. Practice the deep probe explanation out loud. The vocabulary has to be automatic, not retrieved.
Closures are foundational to JavaScript interviews. They're foundational to the language. Master them and the rest of JavaScript is easier. Skip them and every interview will keep coming back to them.