JavaScript Closures, Explained (With Examples)
A closure is a function that remembers the variables from where it was created. Here's what closures are in JavaScript, why they matter, and the interview questions they power — with clear examples.
A closure is a function bundled together with the variables that were in scope where it was defined. Even after the outer function has returned, the inner function still “remembers” and can use those variables — that memory is the closure.
Closures are the single most-asked concept in JavaScript interviews, and they quietly power counters, private data, callbacks, and most of the patterns you already use. Once the mental model clicks, a whole category of “why does this work?” questions answers itself.
One-sentence definition: a closure is a function plus the lexical environment it was created in.
A minimal example
function makeCounter() {
let count = 0; // a local variable
return function () {
count += 1; // the inner function uses it
return count;
};
}
const next = makeCounter();
console.log(next()); // 1
console.log(next()); // 2
console.log(next()); // 3
makeCounter has already returned by the time we call next(). In most languages, count would be gone — the function finished, its local variables cleaned up. But in JavaScript the returned function closed over count, so it stays alive, private, and persistent between calls.
That is a closure: next is a function that carries count with it.
Why does this work? Lexical scope
JavaScript uses lexical scope, which means a function’s access to variables is determined by where it is written in the code, not where it is called from. When you define a function, it gets a hidden link to the scope around it. Following that chain outward is how variable lookups resolve.
So when next() runs and asks for count, JavaScript looks:
- Inside
nextitself — not found. - In the scope where
nextwas defined (insidemakeCounter) — found it.
That outward chain is the scope chain, and the closure is what keeps the relevant link from being garbage-collected.
The classic interview gotcha: a loop with var
This is the example interviewers love, because it tests closures and scope at once.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// logs: 3, 3, 3 — not 0, 1, 2!
Why three 3s? var is function-scoped, so there is only one i, shared by all three callbacks. By the time the timeouts fire, the loop has finished and i is 3. Each closure remembers the same variable, not a snapshot.
The fix is let, which is block-scoped — a fresh i for each iteration:
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// logs: 0, 1, 2
This one example is why we tell beginners to default to let/const and treat var as legacy. It also shows up constantly in interviews, so make sure you can explain why, not just patch it.
What closures are good for
Closures are not a trick — they are a tool you will reach for often.
- Private state / data privacy. Variables inside the outer function are unreachable from outside; only the methods you return can touch them. This is encapsulation without classes.
- Function factories. Generate specialised functions from a template:
const multiplier = (factor) => (n) => n * factor;
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5), triple(5)); // 10 15
- Remembering state between calls — counters, accumulators, rate limiters, memoisation caches.
- Callbacks and event handlers that need access to surrounding data:
function setupButton(label) {
button.addEventListener("click", () => {
console.log(`You clicked: ${label}`); // closes over `label`
});
}
Every time you pass an arrow function to map, filter, setTimeout, or addEventListener and it uses a surrounding variable, you are using a closure.
A mental model that sticks
Think of a closure as a backpack. When a function is created, it packs a backpack containing every variable it can see from where it was born. Wherever the function travels — returned, passed as a callback, stored for later — it carries that backpack and can reach inside it.
The outer function returning does not empty the backpack. Only when nothing references the inner function anymore can the contents be cleaned up.
Common mistakes
- Assuming the outer variables are copied. They are not — closures hold a live reference. Change the variable later and the closure sees the new value (that is the
varloop bug). - Over-capturing big objects. If a long-lived closure references a huge array it no longer needs, that array cannot be garbage-collected. Null it out or scope it tighter.
- Confusing closures with
this. They are different mechanisms. Closures are about variable scope;thisis about how a function is called. See Thethiskeyword, finally explained.
How closures fit the bigger picture
Closures sit in Phase 5 of our JavaScript roadmap — the “tricky internals” that separate someone who can follow a tutorial from someone who actually knows the language. They also underpin asynchronous patterns, since every callback closes over its surroundings; see Promises and async/await, explained.
If you want this drawn out as diagrams — scope chains, the backpack model, the loop bug — that is exactly the depth in JavaScript in Three Months, our job-ready tier. For a faster pass, JavaScript in One Month covers closures with worked examples — and JavaScript for Staff Engineers pushes into what closures unlock: the module pattern, currying, memoization, and the performance trade-offs behind them.
Master closures and you have crossed the line from “writes JavaScript” to “understands JavaScript.”
Frequently asked questions
What is a closure in JavaScript, in simple terms?
A closure is a function bundled together with the variables that were in scope when it was created. Even after the outer function has finished running, the inner function still has access to those variables. In short: a function that 'remembers' where it was born.
Why are closures useful?
Closures let you create private variables, build function factories, remember state between calls (like a counter), and write callbacks and event handlers that keep access to their surrounding data. They are the foundation of data privacy and many common patterns in JavaScript.
Are closures a common interview question?
Yes — closures are one of the most frequently asked JavaScript interview topics, often paired with scope, hoisting, and the classic 'loop with var' bug. Expect to be asked to define a closure and to predict the output of a counter or loop example.
Do closures cause memory leaks?
They can, if a closure keeps a reference to a large object that is no longer needed, preventing garbage collection. In normal code this is rarely a problem; just be mindful when closures capture big data structures inside long-lived callbacks or event listeners.