A #name declaration creates a Private Name: a unique key created each time the class is evaluated and visible only inside the class body. Fields, methods, accessors and their static forms can all be private. Each object stores its private elements in a separate [[PrivateElements]] list, not among its properties.
What follows from that:
- Real encapsulation. Outside the class,
obj.#xis aSyntaxErrorat parse time, andobj['#x']is just an ordinary string key.delete obj.#xis aSyntaxErrortoo. - Invisible to reflection.
Object.keys,Reflect.ownKeys,JSON.stringify, andstructuredClonedon't see them. - Not properties, so no property machinery.
Object.freezedoesn't stop writes to private fields. Proxies can't intercept them:new Proxy(obj, {}).#xlooks the Private Name up on the proxy object itself, which has none, so it throws aTypeError. - Own-object only. Lookup doesn't walk the prototype chain. A subclass can't touch a parent's
#xdirectly, though inherited methods that use it keep working on subclass instances, because the parent constructor installed it onthis. - Brand checks. Reading a missing private element throws a
TypeError.#x in objreturns a boolean instead, which is the safe "is this really one of ours?" test (it throws for non-objects). - Private methods are shared, not copied per instance; the spec just records which objects have been branded with them.
Compared with alternatives: _name is a convention anyone can bypass, a Symbol key is still discoverable via getOwnPropertySymbols, and closures give true privacy but allocate per-instance functions and can't be shared with prototype methods.