How do closures create private state?
By FrontendPro Editorial Team Updated 9/26/2026
Answer
A variable declared inside a function is unreachable from outside. If that function returns something that still uses the variable, the variable stays alive but remains unreachable — that is private state.
function createCounter() {
let count = 0; // no outside reference to this exists
return {
increment: () => ++count,
get value() { return count; },
};
}
const c = createCounter();
c.increment();
c.value; // 1
c.count; // undefined — there is no such property
count is not hidden by convention; it is genuinely inaccessible. The only way to touch it is through the methods you chose to return, which is what makes the pattern useful: you control every path that can change the value.
Each call gets its own
const a = createCounter();
const b = createCounter();
a.increment();
b.value; // 0 — independent state
This is the difference from a module-level variable, which would be shared by everyone.
The modern alternative
Class private fields do the same job with clearer syntax, and they work with instanceof and inheritance:
class Counter {
#count = 0;
increment() { return ++this.#count; }
get value() { return this.#count; }
}
Reading obj.#count from outside is a syntax error, not merely undefined.
Which to use
Closures suit factory functions and single instances, and they are the only option in plain functional code. Private fields suit anything already written as a class. Note the older _name convention is not privacy at all — it is a comment that anyone can ignore.
One caveat: a closure keeps everything in its scope alive, not just the variables you use. Holding a reference to a large object inside a long-lived closure keeps that object from being collected, which is a common source of memory leaks in event handlers.
