JavaScript Interview Questions on Closures and Captured Values
A JavaScript closure lets a function keep access to the lexical environment where it was created. The function does not simply receive a frozen photograph of every variable. To explain an output question, identify the binding each call reads and when that read happens.
By Ravindra Wadile · Published

622 words · About 3 min read
A JavaScript closure lets a function keep access to the lexical environment where it was created. The function does not simply receive a frozen photograph of every variable. To explain an output question, identify the binding each call reads and when that read happens.
Do two counter functions share one count?
Not when they come from separate calls to this factory. Each call creates a new environment for its own count. The returned function can still access that environment after the factory has returned.
function makeCounter(start = 0) {
let count = start;
return function next() {
count += 1;
return count;
};
}
const first = makeCounter(2);
const second = makeCounter(2);
console.assert(first() === 3);
console.assert(first() === 4);
console.assert(second() === 3);
The result is not caused by a global variable or an object property named count. It follows from two separate factory calls. If two variables referenced the same returned function instead, they would use that function's shared environment.
Does a closure remember a value or a binding?
Consider an outer let variable changed after an inner function is created. When that inner function later reads the binding, it can observe the changed value. If you deliberately compute a separate string first, that string is a different value with a different binding.
function makeStatus() {
let status = "draft";
const initialLabel = "Initial: " + status;
return {
update(value) { status = value; },
current() { return status; },
label() { return initialLabel; }
};
}
const item = makeStatus();
item.update("ready");
console.assert(item.current() === "ready");
console.assert(item.label() === "Initial: draft");
Both functions are closures. They read different bindings. Saying “closures always have stale values” misses that difference. Trace which expression ran at creation time and which expression runs on each later call.
Why do loop callbacks sometimes surprise people?
A loop with a shared var binding differs from one with per-iteration let bindings. If callbacks run later, their lookup may happen after the loop has moved on. Do not answer solely from indentation; read the declaration and determine whether each callback has its own relevant binding.
You can practise without timers by creating functions inside a loop and calling them afterwards. That removes scheduling from the exercise. Once you understand the bindings, introduce asynchronous execution as a separate variable.
Are closures only useful for hiding state?
No. Event handlers, function factories and callbacks can also use them. Hiding state is one possible design use, but it does not automatically make a module secure or an object immutable. The returned functions may still allow every important state change.
Also consider lifetime. If a long-lived callback retains an environment that references a large object, the object may remain reachable. A closure is not inherently a leak, but a forgotten listener can keep data alive longer than intended.
How should you explain this in an interview?
- Name the outer invocation that created the environment.
- Identify the exact binding the inner function reads.
- Describe any changes before the later call.
- State the expected output and show a small check.
If the interviewer asks about React state, make the next boundary explicit: a function produced by one render can read that render's values. That framework situation builds on lexical scope, but it needs its own rendering explanation. It does not change the general JavaScript rule.
A useful practice variation
Assign the first counter to a third variable and call both names. Explain why that differs from calling the factory again. Then modify the status factory so label builds its string on every call. The changed output should follow from a changed read, not a new memorized exception.
References and next practice
developer.mozilla.org — js closure
Browse interview topics · Practise a question in Interview Challenge · How to Answer Technical Interview Questions Out Loud