A Minimal Example
makeCounter() runs and returns. Normally, count would be garbage collected once the function exits. But because the returned inner function still references count, JavaScript keeps it alive. Every call to counter() shares the same remembered count variable.
Why This Happens: Lexical Scope
JavaScript uses lexical scoping — a function's access to variables is determined by where it was written in the code, not where it is called from. When a function is defined inside another function, it keeps a live reference to the outer function's variables. That live reference is the closure.
The Classic var-in-a-loop Bug
var is function-scoped, not block-scoped, so all three callbacks share a single i variable. By the time the timeouts fire, the loop has already finished and i is 3.
let is block-scoped — each loop iteration gets its own fresh binding, so each closure captures a different i.
⚠️ This Is a Top Interview Question
"Why does this loop print the same number three times, and how do you fix it?" is one of the most commonly asked JavaScript interview questions — because it tests whether you actually understand closures, not just whether you can define the word.
Common Real-World Uses of Closures
| Use Case | How Closures Help |
|---|---|
| Private state | Variables in an outer function become inaccessible from outside, simulating private fields |
| Memoization / caching | An inner function can read and update a cache object held in the outer scope |
| Event handlers | A handler can remember data from when it was attached, without global variables |
| Currying / partial application | A function returns another function pre-loaded with some arguments already "remembered" |
| Module pattern | An IIFE returns an object whose methods close over private internal state |
Private State Example
Closures and Memory
Because a closure keeps its outer scope alive, anything referenced by a long-lived closure cannot be garbage collected. This is usually harmless, but it becomes a memory leak when a closure attached to a DOM element or event listener is never cleaned up — the closure (and everything it references) stays in memory for the lifetime of the page.
💡 Mental Model
Think of a closure as a backpack. Every function carries a backpack containing the variables it could see when it was created. The function can open that backpack and use those variables no matter where it is later called from.
How We Research and Update This Guide
We test the underlying formula or workflow, compare outputs with reliable references, and revise examples whenever the page content changes.
- The workflow or formula is tested directly in the tool and compared against independent reference examples.
- Examples are kept practical so readers can verify the result without hidden assumptions.
- Pages are revised whenever the interface, calculation flow, or surrounding guidance materially changes.
Frequently Asked Questions — Closures
A closure is a function that remembers the variables from the place it was defined, even after that outer function has finished running. In JavaScript, every function forms a closure over its surrounding scope — it "closes over" the variables it can see, keeping them alive in memory for as long as the function itself exists.
Closures explain a huge range of JavaScript behaviour: why a callback inside a loop sees the wrong value, how private variables work without classes, how memoization caches work, and why event handlers can "remember" state. Interviewers use closures to test whether you understand scope and execution context, not just syntax.
Using var in a for-loop with an asynchronous callback (like setTimeout) causes every callback to print the same final value, because var is function-scoped, not block-scoped — all callbacks share the same single variable. Switching to let creates a new binding per iteration, fixing the bug. This is one of the most common closure interview questions.
Closures are how JavaScript implements private state, but they are not the same concept. A closure is just a function plus its remembered scope. You use that mechanism to build "private" variables by returning inner functions from an outer function — the outer variables become inaccessible from outside, but the returned functions can still read and modify them.
They can. Because a closure keeps its outer scope alive, any large object referenced by a long-lived closure (e.g. a closure attached to a DOM event listener that never gets removed) will not be garbage collected. This is a common source of memory leaks in single-page applications — always remove event listeners and clear timers you no longer need.
Scope is the set of variables accessible at a given point in code — it is a static, lexical concept. A closure is the runtime mechanism that lets a function retain access to its scope even after the outer function has returned. Scope is the rule; a closure is the live binding that the rule produces at runtime.