Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Успадкування приватних полів». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Ні, приватні поля не успадковуються: `#field` належить тілу конкретного класу, а не ланцюжку прототипів, тому звернення `this.#secret` усередині підкласу є `SyntaxError`.** Підклас успадковує публічні та захищені за домовленістю члени і може викликати батьківські методи, які самі читають приватне поле, але самого поля він не бачить. Якщо дані мають бути доступні нащадкам, використовують getter або метод батька, домовленість `_field` чи `WeakMap`. ```javascript class Parent { #secret = 'parent secret'; getSecret() { return this.#secret; } } class Child extends Parent { reveal() { return this.getSecret(); // працює, через публічний метод батька // return this.#secret; // SyntaxError, поле не належить Child } } ``` **Ключове:** кожен клас має власний приватний простір імен, тому `extends` дає методи, але не дає доступу до `#field` батька.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Приватні поля в JavaScript не успадковуються: `#field` прив'язане до тіла того класу, де його оголошено, тому підклас не може написати `this.#field`.** Такий вираз, це `SyntaxError` ще на етапі парсингу, навіть якщо об'єкт фізично містить це поле, бо його створив конструктор батька. ## Теорія ### TL;DR - `#field` належить одному конкретному тілу класу, а не прототипу, тому підклас його не бачить. - Звернення до `this.#parentField` усередині нащадка, це `SyntaxError`, а не `undefined`. - Однакові імена `#secret` у батька і в нащадка, це два різні незалежні поля. - Публічні методи батька, що читають своє приватне поле, успадковуються і чудово працюють на екземплярі нащадка. - Щоб поділитися даними з нащадками, використовують getter або метод, домовленість `_field` чи `WeakMap`. ### Швидкий приклад ```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', метод батька бачить своє приватне поле ``` Екземпляр `Child` справді має поле `#secret` усередині, бо його встановив конструктор `Parent`. Але код класу `Child` не має права назвати це ім'я, тому рушій навіть не компілює такий модуль. ### Як це працює під капотом Коли ви оголошуєте `#field`, JavaScript створює унікальний прихований ідентифікатор, доступний лише всередині тіла цього класу. Приватне ім'я, це не рядок і не Symbol у звичайному сенсі: воно резолвиться лексично, як змінна, а не через властивість об'єкта. Наслідок: однакові імена в різних класах, це різні поля без жодного зв'язку між собою. ```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`, друге «ключем» класу `Child`. Жодного затінення чи конфлікту не відбувається. ### Чому мова заборонила таке успадкування Це свідоме рішення авторів специфікації: - **Сувора інкапсуляція.** Усе, що починається з `#`, приховане повністю, і його не можна відкрити ззовні через створення підкласу. - **Відсутність конфліктів імен.** Автор бібліотеки може додати `#cache` у базовий клас і не зламати нащадка, який теж має `#cache`. - **Простіша оптимізація в рушіях.** V8, SpiderMonkey та JavaScriptCore знають повний набір приватних імен класу під час компіляції і можуть розкласти їх у фіксовані слоти. Тому `extends` успадковує методи та публічні поля, але не дає доступу до приватних полів батька. ### Як поділитися даними з нащадками Справжнього модифікатора `protected` у JavaScript немає, тому використовують один з трьох підходів. **Варіант 1: домовленість `_field`.** Це не приватність, а угода «не чіпай ззовні», зате поле звичайне і повністю доступне нащадкам. ```javascript class Parent { _secret = 'available to subclasses'; showSecret() { console.log(this._secret); } } class Child extends Parent { reveal() { console.log(this._secret); // працює } } ``` **Варіант 2: getter або метод батька.** Приватність зберігається, а нащадок працює через інтерфейс і не знає деталей реалізації. ```javascript class Parent { #secret = 'parent secret'; getSecret() { return this.#secret; } } class Child extends Parent { reveal() { console.log(this.getSecret()); // 'parent secret' } } new Child().reveal(); ``` **Варіант 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); // 'shared secret' } } new Child().reveal(); ``` `WeakMap` не тримає сильного посилання на ключ, тому запис зникає разом зі знищенням екземпляра і витоку пам'яті не буде. ### Порівняння підходів | Підхід | Приватність | Доступ нащадкам | Реальний захист | | --- | --- | --- | --- | | `#field` | повна | ні | так, на рівні синтаксису | | `_field` (домовленість) | часткова | так | ні, лише угода | | `WeakMap` | майже повна | так | так, дані поза об'єктом | | getter або метод | через інтерфейс | так | так, контрольовано | ### Типові помилки - **Очікувати `undefined` замість помилки.** Звернення до чужого `#field`, це `SyntaxError` на етапі парсингу, тому падає весь модуль, а не один виклик. - **Вважати, що однакове ім'я означає одне поле.** `#secret` у батька і `#secret` у нащадка, це два незалежні слоти; перевизначити батьківське поле в такий спосіб неможливо. - **Думати, що методи теж не успадковуються.** Публічні методи батька успадковуються нормально і всередині себе вільно читають власне приватне поле. - **Шукати `protected` у JavaScript.** Такого модифікатора немає; `protected` у TypeScript, це перевірка часу компіляції, у зібраному JS поле лишається публічним. - **Оголошувати приватне поле лише в конструкторі нащадка.** Приватне ім'я має бути оголошене в тілі класу, інакше присвоєння `this.#field` теж буде синтаксичною помилкою. - **Плутати `#field` з приватністю через `WeakMap`.** `#field` недоступне навіть у підкласі, а `WeakMap` віддає дані будь-кому, хто має посилання на саму мапу.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.