Closures
A closure is a function that remembers the variables from the place it was created, even after that place is "gone."
What Is a Closure?
Every time a function is created inside another function, it keeps a link back to the variables that were around it at that moment. That link — the inner function plus the memory of its surroundings — is called a closure. This happens automatically; you don't need any special syntax to create one.
function makeCounter() {
let count = 0;
return function() {
count = count + 1;
return count;
};
}
const counter = makeCounter();
counter(); // 1
counter(); // 2
counter(); // 3
makeCounterruns once, createscount, and returns an inner function- Normally,
countwould disappear oncemakeCounterfinishes running - But the inner function closes over
count, keeping it alive in memory - Each call to
counter()reaches into that same preservedcountand updates it
Imagine a backpack. When a function is created inside another function, it packs a backpack with copies of the variables it might need later, then carries that backpack wherever it goes — even long after the place it was packed in no longer exists. Every time you call the function, it reaches into its own backpack.
Why Do Closures Matter?
- They let you create private variables —
countabove can't be accessed or changed directly from outside, only through the returned function. - They power many common patterns: counters, caches, event handlers that remember data, and the module pattern.
- They're the mechanism behind most "state" in JavaScript before frameworks like React existed.
When Should We Use Them?
Any time you want a function to "remember" something between calls, without storing that thing in a global variable everyone can accidentally change, reach for a closure.
A Second Example: Private Data
function createAccount(startingBalance) {
let balance = startingBalance; // not accessible from outside
return {
deposit(amount) { balance += amount; },
getBalance() { return balance; }
};
}
const account = createAccount(100);
account.deposit(50);
account.getBalance(); // 150 — balance itself is hidden from outside code
Common Mistakes
- The classic
var-in-a-loop bug: becausevarisn't scoped per iteration, closures created in a loop withvarall end up sharing the same final value. Usingletfixes this, sinceletcreates a fresh variable each iteration. - Assuming closures copy a variable's value — they don't; they keep a live link to the actual variable, so later changes are visible too.
- Creating closures unnecessarily in hot loops, which can hold onto memory longer than needed.
// The classic bug
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // logs 3, 3, 3
}
// Fixed with let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // logs 0, 1, 2
}
Best Practices
- Use closures to hide implementation details and expose only what's needed (encapsulation).
- Prefer
let/constovervarso closures in loops behave as expected. - Don't over-nest functions purely to create closures — use them when they solve a real problem.
Performance Notes
Closures keep their captured variables in memory for as long as the closure itself is reachable, which can slightly increase memory use compared to plain variables that get garbage collected right away. This is rarely a real problem, but worth knowing for long-lived closures holding large data.
Browser Compatibility
Closures are a fundamental part of JavaScript and have worked identically in every browser since the language existed.
Interview Questions
What is a closure?
A function combined with references to the variables from the scope it was created in, allowing it to access those variables even after the outer function has finished running.
Give a practical use case for closures.
Creating private state, such as a counter or bank balance, that can only be read or changed through specific functions rather than direct access.
✏️ Exercise
Write a function makeGreeter(greeting) that returns a new function which takes a name and returns `${greeting}, ${name}!`. Create two greeters with different greetings and prove they don't interfere with each other.
🧠 Quiz
1. Does a closure store a copy of a variable, or a live link to it?
A live link — changes to the original variable are visible through the closure.
2. Why does using var instead of let in a loop cause bugs with closures?
var is function-scoped, not block-scoped, so every closure created in the loop shares the exact same variable instead of getting its own copy per iteration.
🚀 Mini Challenge
Build a createToggle() function that returns a function which flips between true and false every time it's called, starting at false.
Summary
- A closure is a function that remembers variables from where it was defined.
- Closures enable private state and are created automatically, with no special syntax.
- Watch for the classic
var-in-a-loop pitfall — useletinstead.
Related Topics
MDN reference: developer.mozilla.org → JavaScript → Closures (placeholder — add live link)