Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як працюють приватні поля й методи класів через #?

Префікс # робить поле, метод чи аксесор класу справді приватним: доступ до нього можливий лише з коду всередині тіла класу.

class Cart {
  #items = [];
  static #instances = 0;

  constructor() {
    Cart.#instances++;
  }

  add(product, qty = 1) {
    this.#validate(qty);
    this.#items.push({ product, qty });
  }

  get total() {
    return this.#items.reduce((sum, { product, qty }) => sum + product.price * qty, 0);
  }

  #validate(qty) {
    if (qty <= 0) throw new RangeError('Кількість має бути додатною');
  }
}

const cart = new Cart();
cart.add({ price: 100 }, 2);
cart.total;     // 200
cart.#items;    // SyntaxError - навіть не помилка під час виконання, а помилка розбору

Чим відрізняється від конвенції _items:

  • _items - лише домовленість: властивість доступна, видна в Object.keys, у JSON.stringify, її можна змінити ззовні;
  • #items - приватність гарантована мовою: не видно в Object.keys, не серіалізується в JSON, не доступна через obj['#items'], проксі й рефлексію.

Корисні можливості:

  • перевірка «бренду» - чи об'єкт створено цим класом, без instanceof (якого можна обдурити підміною прототипу):
class Money {
  #amount;
  static isMoney(value) {
    return #amount in value;
  }
}
  • статичні приватні поля й методи (static #instances);
  • статичні блоки ініціалізації (static { ... }) - мають доступ до приватних елементів класу.

Обмеження й підводні камені:

  • приватні поля не наслідуються в доступі: підклас не бачить #items батьківського класу. Для «захищених» (як protected у PHP) окремого синтаксису немає;
  • Proxy над об'єктом з приватними полями ламає методи: метод, викликаний через проксі, отримує this = проксі, а в проксі немає приватних полів - TypeError. Це помітно в реактивних системах: Vue робить об'єкти реактивними через Proxy, тож класи з # полями в reactive() можуть падати. Для таких класів - markRaw чи shallowRef;
  • приватні поля оголошуються в тілі класу - додати їх динамічно неможливо;
  • structuredClone і JSON.stringify їх не копіюють.

TypeScript має і модифікатор private, і #. private - лише перевірка компілятора, після компіляції поле звичайне; # - справжня приватність під час виконання.

Докладніше в документації: Приватні елементи класу

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання