JavaScript Closures Explained (With Interview Questions)
Closures in plain English: lexical scope, how closures work, common interview traps (loops + setTimeout), and practical use cases like memoization and data hiding.
Frontend Interview Team
February 08, 2026
A closure is one of those interview topics that sounds scary until you realize it’s just this:
A function “remembers” the variables from the place it was created.
30‑second interview answer
A closure is created when a function is defined inside another scope and references variables from that outer scope. Even after the outer function finishes, the inner function can still access those variables because JavaScript keeps the lexical environment alive as long as it’s reachable.
Key points
- Closures come from lexical scope, not async.
- Closures preserve access to variables (not necessarily copying values).
- They’re used for private state, factories, memoization, and event handlers.
That place is called lexical scope.
1) The smallest closure example
function outer() {
const name = 'Rajesh';
function inner() {
console.log(name);
}
return inner;
}
const fn = outer();
fn(); // RajeshEven though outer() finished, inner() still has access to name.
That’s the closure.
2) What closures are NOT
Closures are not:
- a special syntax
- only for async
- only for React hooks
Closures happen because of:
- lexical scoping (scope is decided by where code is written)
- functions being first-class values
3) Interview question: “Why does this print 3,3,3?”
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}Explanation
varis function-scoped (not block-scoped).- All callbacks close over the same
i. - By the time the timeouts run,
iis3.
Fix #1: use let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// 0 1 2Because let creates a new binding per loop iteration.
Fix #2: IIFE (older pattern)
for (var i = 0; i < 3; i++) {
(function (x) {
setTimeout(() => console.log(x), 0);
})(i);
}4) Practical use cases (what interviewers like)
A) Data hiding / private state
function createCounter() {
let count = 0;
return {
inc() { count++; return count; },
dec() { count--; return count; },
get() { return count; }
};
}
const c = createCounter();
c.inc(); // 1
c.get(); // 1Here, count is private. Only the returned methods can access it.
B) Memoization (cache results)
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}C) Function factories
function multiplyBy(n) {
return function (x) {
return x * n;
};
}
const double = multiplyBy(2);
double(10); // 205) Common closure bug in React
In React, closures often show up as “stale state”.
Example pattern:
- you use a value inside an effect
- but your dependency array doesn’t include it
Then your function closes over an old value.
(We’ll cover this properly in the React hooks section.)
6) Rapid-fire interview Q&A
Q1: What is a closure?
- A function + its lexical environment (the variables it can access).
Q2: When is a closure created?
- When a function is defined inside another scope and references outer variables.
Q3: Does closure mean the variables are copied?
- No, usually it keeps a reference to the environment.
Q4: Is closure only for async?
- No. Async just makes it more visible.
Q5: Can closures cause memory leaks?
- Yes, if you keep references to large objects longer than needed.
Summary checklist
- I can define closure in one sentence.
- I can explain the
forloop +setTimeouttrap. - I can give 2 real-world use cases (private state, memoization, factories).
- I know closures can keep memory alive if references persist.
Summary
- Closures = functions remembering outer variables.
letfixes most loop closure issues.- Interviewers love when you connect closures to real use cases: private state, memoization, factories.