QuestionsJavaScript

Private class fields and methods (#)

Class EncapsulationMediumJavaScript

How do #private class fields work, and how do they differ from closures, WeakMap privacy, or a leading-underscore convention?

What it tests

Real language-level encapsulation vs conventions, plus the brand-check idiom.

Approach & answer

A field or method prefixed with `#` is TRULY private: it's accessible only inside the class body, enforced by the language (not a convention). Unlike a leading `_name`, `#name` is invisible to outside code, to `Object.keys`, to `for...in`, to JSON.stringify, and to Proxies — reaching for `obj.#x` outside the class is a SYNTAX error, caught at parse time, not a runtime undefined. This beats the older patterns: the leading-underscore convention is just discipline (anyone can still touch it); closure-based privacy (capturing vars in the constructor) truly hides state but creates a fresh copy of every method per instance (memory cost) and can't be shared on the prototype; the WeakMap pattern works and predates `#` but is verbose. Private members can be fields, methods, getters/setters, and STATIC (`static #count`). A powerful idiom is the brand check: `#field in obj` is a boolean that tells you whether obj was constructed by this class (it has the private slot) WITHOUT throwing — useful for `static isInstance(x)` guards that work even across realms where instanceof is unreliable. Caveats: private names aren't reflectable (that's the point — no metaprogramming access), and they're per-class, so a subclass can't see the parent's `#` members.

Use this technique when

Enforcing invariants no consumer can bypass, hiding internal state from serialisation/Proxies, and writing robust type-guard helpers via brand checks.

References

js