Чи можна успадковувати приватні властивості?
1. Приклад: приватне поле в батьківському класі недоступне в нащадку
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 у двох класах,
це два різних поля, не пов'язані між собою:
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)
Це не справжня приватність, але часто використовується на практиці:
class Parent {
_secret = "accessible to subclasses";
showSecret() {
console.log(this._secret);
}
}
class Child extends Parent {
reveal() {
console.log(this._secret); // Працює
}
}Тут
_secret, це просто домовленість "не чіпай цю властивість ззовні". Вона не захищена, але доступна нащадкам.
Варіант 2: використати геттери / сеттери
Батьківський клас може керувати доступом через методи, а нащадок може використовувати ці методи, не знаючи деталей реалізації.
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)
Це старий, але гнучкий спосіб "приватних" даних із контрольованим доступом.
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 secretWeakMap дає змогу приховати дані, але водночас робити їх доступними в нащадках.
5. Підсумок: порівняння підходів
| Підхід | Приватність | Доступ нащадкам | Реальний захист |
|---|---|---|---|
#field | Повна | Немає | Абсолютний |
_field (за домовленістю) | Часткова | Так | Захисту немає |
WeakMap | Майже повна | Так | Так |
| Геттер/сеттер | Через інтерфейс | Так | Контрольований |
Короткий висновок
Приватні поля (
#field) не успадковуються. Кожен клас має свій власний приватний простір. Нащадки не можуть напряму звертатися до приватних полів батьківського класу. Якщо потрібно поділитися приватними даними, використай:
_field(домовленість),- геттери/сеттери,
- або
WeakMapіз доступом для нащадків.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.