Suggest an editImprove this articleRefine the answer for “Inheriting private class fields”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**No, private fields are not inherited: `#field` belongs to one specific class body, not to the prototype chain, so `this.#secret` inside a subclass is a `SyntaxError`.** A subclass does inherit public and convention-protected members and can call parent methods that read the private field themselves, but it cannot see the field. When descendants need the data, you expose it through a parent getter or method, use the `_field` convention, or store it in a `WeakMap`. ```javascript class Parent { #secret = 'parent secret'; getSecret() { return this.#secret; } } class Child extends Parent { reveal() { return this.getSecret(); // works, through the parent public method // return this.#secret; // SyntaxError, the field does not belong to Child } } ``` **Key point:** every class has its own private namespace, so `extends` gives you methods but never access to the parent `#field`.Shown above the full answer for quick recall.Answer (EN)Image**Private fields in JavaScript are not inherited: `#field` is bound to the body of the class that declares it, so a subclass cannot write `this.#field`.** That expression is a `SyntaxError` at parse time, even though the object physically carries the field because the parent constructor created it. ## Theory ### TL;DR - `#field` belongs to one specific class body, not to the prototype, so a subclass cannot see it. - Accessing `this.#parentField` inside a descendant is a `SyntaxError`, not `undefined`. - The same `#secret` name in a parent and in a child is two different, independent fields. - Public parent methods that read their own private field are inherited and work fine on a child instance. - To share data with descendants you use a getter or method, the `_field` convention, or a `WeakMap`. ### Quick example ```javascript class Parent { #secret = 'parent secret'; showSecret() { console.log(this.#secret); } } class Child extends Parent { reveal() { // console.log(this.#secret); // SyntaxError: Private field '#secret' must be declared in an enclosing class } } const c = new Child(); c.showSecret(); // 'parent secret', the parent method sees its own private field ``` A `Child` instance really does hold `#secret` inside, because the `Parent` constructor set it. But the code of `Child` has no right to spell that name, so the engine refuses to compile the module at all. ### How it works under the hood When you declare `#field`, JavaScript creates a unique hidden identifier that is available only inside that class body. A private name is not a string and not a Symbol in the usual sense: it resolves lexically, like a variable, not through an object property lookup. The consequence: identical names in different classes are different fields with no relation to each other. ```javascript class Parent { #secret = 'parent'; getParentSecret() { return this.#secret; } } class Child extends Parent { #secret = 'child'; getChildSecret() { return this.#secret; } } const c = new Child(); console.log(c.getParentSecret()); // 'parent' console.log(c.getChildSecret()); // 'child' ``` One object happily holds two fields named `#secret`: one keyed by the `Parent` class, one by the `Child` class. There is no shadowing and no conflict. ### Why the language forbids this inheritance It is a deliberate decision by the specification authors: - **Strict encapsulation.** Everything that starts with `#` is fully hidden and cannot be opened up by subclassing. - **No name collisions.** A library author can add `#cache` to a base class without breaking a subclass that also has `#cache`. - **Easier engine optimisation.** V8, SpiderMonkey and JavaScriptCore know the full set of private names of a class at compile time and can lay them out in fixed slots. So `extends` inherits methods and public fields, but never gives access to the parent private fields. ### How to share data with descendants JavaScript has no real `protected` modifier, so one of three approaches is used. **Option 1: the `_field` convention.** This is not privacy, it is an agreement not to touch the property from outside, but the field is ordinary and fully available to subclasses. ```javascript class Parent { _secret = 'available to subclasses'; showSecret() { console.log(this._secret); } } class Child extends Parent { reveal() { console.log(this._secret); // works } } ``` **Option 2: a parent getter or method.** Privacy is preserved, and the subclass works through the interface without knowing implementation details. ```javascript class Parent { #secret = 'parent secret'; getSecret() { return this.#secret; } } class Child extends Parent { reveal() { console.log(this.getSecret()); // 'parent secret' } } new Child().reveal(); ``` **Option 3: a `WeakMap`.** The classic pre-ES2022 privacy technique. The data lives outside the object, in a module closure, so it is unreachable from outside, yet the parent can expose it to descendants through a protected accessor. ```javascript const privates = new WeakMap(); class Parent { constructor() { privates.set(this, { secret: 'shared secret' }); } get _data() { return privates.get(this); } } class Child extends Parent { reveal() { console.log(this._data.secret); // 'shared secret' } } new Child().reveal(); ``` A `WeakMap` holds no strong reference to its key, so the entry disappears together with the instance and there is no memory leak. ### Comparing the approaches | Approach | Privacy | Available to subclasses | Real protection | | --- | --- | --- | --- | | `#field` | full | no | yes, enforced by syntax | | `_field` (convention) | partial | yes | no, convention only | | `WeakMap` | almost full | yes | yes, data lives outside the object | | getter or method | through the interface | yes | yes, controlled | ### Common mistakes - **Expecting `undefined` instead of an error.** Touching someone else's `#field` is a parse time `SyntaxError`, so the whole module fails, not just that one call. - **Assuming the same name means the same field.** `#secret` in the parent and `#secret` in the child are two independent slots; you cannot override the parent field that way. - **Thinking methods are not inherited either.** Public parent methods are inherited normally and freely read their own private field inside. - **Looking for `protected` in JavaScript.** There is no such modifier; `protected` in TypeScript is a compile time check, and in the emitted JS the field stays public. - **Declaring a private field only in the child constructor.** The private name must be declared in the class body, otherwise assigning `this.#field` is a syntax error as well. - **Confusing `#field` with `WeakMap` privacy.** `#field` is unreachable even in a subclass, while a `WeakMap` hands the data to anyone who holds a reference to the map itself.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.