Closures
A function that remembers the variables around it, even after the outer function has finished. Strange at first, but the secret behind counters, private data and most event handlers — and the most-asked JS interview question.
What you will learn
- Define a closure in plain words
- Build a counter that keeps private state
- Recognise closures you already use
A function with a memory
Normally, when a function finishes, its local variables disappear. But if that function returns another function (or hands one off as a handler), the inner function keeps a live link to the variables it was born with — even after the outer function is long gone. That bundle of "function + the variables it remembers" is called a closure.
A simple picture: every function is born carrying a little backpack of the variables that surrounded it. Wherever the function travels, it carries that backpack and can still read and update what is inside.
A counter that keeps its own count
The classic example is a counter factory. makeCounter creates a private count and returns a small function that increases and reports it. The count is not a global — it lives only inside the closure.
<script>
function makeCounter() {
let count = 0; // private to this counter
return function () {
count = count + 1; // remembers and updates count
return count;
};
}
const next = makeCounter();
document.write(next() + ", "); // 1
document.write(next() + ", "); // 2
document.write(next()); // 3
</script>Walk through the magic:
makeCounter()runs, createscount = 0, and returns the inner function. We store that returned function innext.- Even though
makeCounterhas now finished, itscountdid not vanish — the returned function kept it in its backpack (closure). - The first
next()bumpscountto 1 and returns 1. - The second
next()remembers the previous 1 and bumps it to 2; the third returns 3. - The
countis private — no outside code can touch it directly, only throughnext.
Note: Output: 1, 2, 3
And because each call to makeCounter builds a fresh backpack, two counters are completely independent:
<script>
function makeCounter() {
let count = 0;
return () => ++count;
}
const a = makeCounter();
const b = makeCounter(); // a separate, independent counter
document.write(a() + ", " + a() + " | " + b());
</script>Calling makeCounter() twice makes two separate closures, each with its own count. So a goes 1 then 2, while b — untouched by a — starts fresh at 1. This independence is exactly why closures are used to store private, per-instance state.
Note: Output: 1, 2 | 1
You have already been using closures
- Event handlers: a click handler that uses a variable from the surrounding code is a closure — that is how it "remembers" the value when it later fires.
- Private data: closures let you hide variables so only chosen functions can change them — true encapsulation without a class.
- Callbacks & timers: a function passed to
setTimeoutormapcloses over the variables around it.
Tip: Interview-ready definition: "A closure is a function bundled together with references to the variables from the scope where it was created, so it can keep using them even after that scope has returned." Practise saying it with the backpack picture.
Watch out: A subtle classic bug: a var inside a loop is shared by every closure made in that loop, so they all end up seeing the final value. Using let (which is block-scoped — a fresh binding each iteration) fixes it. This is a favourite interview trap.
Q. In makeCounter, why does count keep its value between calls to the returned function?
✍️ Practice
- Build a
makeCounterand prove that two counters keep separate counts. - Write a
makeMultiplier(x)that returns a function multiplying its argument by the rememberedx(e.g.double = makeMultiplier(2)).
🏠 Homework
- Create a tiny "bank account" using a closure: a function that holds a private balance and returns deposit/withdraw functions — no outside code can read the balance directly.