Skip to main content

Чи можна успадковувати приватні властивості?

1. Приклад: приватне поле в батьківському класі недоступне в нащадку

javascript
class Parent { #secret = "parent secret"; showSecret() { console.log(this.#secret); } } class Child extends Parent { reveal() { console.log(this.#secret); // Помилка } } const c = new Child(); c.showSecret(); // "parent secret" c.reveal(); // SyntaxError: Private field '#secret' must be declared in an enclosing class

Пояснення:

  • Приватне поле #secret "належить" лише класу Parent.
  • Клас Child навіть не знає про його існування.
  • Тому доступ до this.#secret всередині Child неможливий.

2. Як це працює під капотом

Коли ти оголошуєш #field, JavaScript створює унікальний прихований ідентифікатор, доступний лише всередині тіла цього класу.

Тобто навіть якщо ти використовуєш однакове ім'я #secret у двох класах, це два різних поля, не пов'язані між собою:

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

Тут #secret у Parent і #secret у Child, це дві незалежні властивості, з однаковим ім'ям, але різними "прихованими ключами".


3. Чому не можна успадковувати приватні поля

Це свідоме рішення авторів мови:

  • Щоб зберегти сувору інкапсуляцію (усе, що починається з #, повністю приховане).
  • Щоб уникнути конфліктів імен між приватними властивостями різних класів.
  • Щоб спростити оптимізацію в рушіях (V8, SpiderMonkey тощо).

Тому extends успадковує методи, але не дає доступу до приватних полів батьківського класу.


4. Як тоді ділитися приватними даними між батьківським класом і нащадком

Якщо ти хочеш, щоб приватні дані були доступні в нащадках, використай один із таких прийомів:


Варіант 1: використати protected-подібну домовленість (_field)

Це не справжня приватність, але часто використовується на практиці:

javascript
class Parent { _secret = "accessible to subclasses"; showSecret() { console.log(this._secret); } } class Child extends Parent { reveal() { console.log(this._secret); // Працює } }

Тут _secret, це просто домовленість "не чіпай цю властивість ззовні". Вона не захищена, але доступна нащадкам.


Варіант 2: використати геттери / сеттери

Батьківський клас може керувати доступом через методи, а нащадок може використовувати ці методи, не знаючи деталей реалізації.

javascript
class Parent { #secret = "parent secret"; getSecret() { return this.#secret; } } class Child extends Parent { reveal() { console.log(this.getSecret()); // Можна отримати через метод } } new Child().reveal(); // parent secret

Таким чином приватність зберігається, але доступ можливий через інтерфейс.


Варіант 3: використати WeakMap (альтернатива # до ES2022)

Це старий, але гнучкий спосіб "приватних" даних із контрольованим доступом.

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); // доступний } } new Child().reveal(); // shared secret

WeakMap дає змогу приховати дані, але водночас робити їх доступними в нащадках.


5. Підсумок: порівняння підходів

ПідхідПриватністьДоступ нащадкамРеальний захист
#fieldПовнаНемаєАбсолютний
_field (за домовленістю)ЧастковаТакЗахисту немає
WeakMapМайже повнаТакТак
Геттер/сеттерЧерез інтерфейсТакКонтрольований

Короткий висновок

Приватні поля (#field) не успадковуються. Кожен клас має свій власний приватний простір. Нащадки не можуть напряму звертатися до приватних полів батьківського класу. Якщо потрібно поділитися приватними даними, використай:

  • _field (домовленість),
  • геттери/сеттери,
  • або WeakMap із доступом для нащадків.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.