22 лютого 2024 р.

Методи прототипів, об’єкти без __proto__

В першій главі цього розділу, ми згадували сучасні методи роботи з прототипом.

Встановлення або читання прототипу за допомогою obj.__proto__ вважається застарілим і не рекомендуються для використання в майбутньому (перенесено до так званого “Annex B” стандарту JavaScript, призначеного лише для браузерів).

Сучасні методи отримання/встановлення прототипу:

Єдине використання __proto__, яке не викликає негативного ставлення, це як властивість під час створення нового об’єкта: { __proto__: ... }.

Хоча і для цього є спеціальний метод:

  • Object.create(proto[, descriptors]) – створює порожній об’єкт із заданим proto як [[Prototype]] і необов’язковими дескрипторами властивостей.

Наприклад:

let animal = {
  eats: true
};

// створюється новий об’єкт з прототипом animal
let rabbit = Object.create(animal); // same as {__proto__: animal}

alert(rabbit.eats); // true

alert(Object.getPrototypeOf(rabbit) === animal); // true

Object.setPrototypeOf(rabbit, {}); // змінює прототип об’єкту rabbit на {}

Object.create має необов’язковий другий аргумент: дескриптори властивостей.

Ми можемо надати додаткові властивості новому об’єкту, як тут:

let animal = {
  eats: true
};

let rabbit = Object.create(animal, {
  jumps: {
    value: true
  }
});

alert(rabbit.jumps); // true

Формат дескрипторів описаний в цій главі Прапори та дескриптори властивостей.

Ми можемо використати Object.create, щоб клонувати об’єкт ефективніше ніж циклом for..in:

let clone = Object.create(
  Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)
);

Таким чином ми створюємо справжню копію об’єкта obj, включаючи всі властивості: перелічувані та не перелічувані, сетери/гетери – з правильним значенням [[Prototype]].

Коротка історія

Якщо порахувати всі способи керування властивістю [[Prototype]], їх буде багато! Багато способів робити одне й те ж саме! Як так сталося? Чому?

Так склалося історично.

Прототипна спадковість була в мові з самого початку, але способи керування нею розвивалися з часом.

  • Властивість prototype функції-конструктора працювала з дуже давніх часів. Це найстаріший спосіб створення об’єктів із заданим прототипом.
  • Пізніше, в 2012 році, метод Object.create став стандартом. Це дало можливість створювати об’єкти з певним прототипом, проте не дозволяло отримувати або встановлювати його. Тому браузери реалізували не стандартний аксесор __proto__, що дозволяв користувачу отримувати та встановлювати прототип в будь-який час, щоб надати розробникам більше гнучкості.
  • Ще пізніше, в 2015 році, методи Object.setPrototypeOf та Object.getPrototypeOf були додані до стандарту, для того, щоб виконувати аналогічну функціональність як і __proto__. Оскільки __proto__ було широко реалізовано, воно згадується в Annex B стандарту як не обов’язкове для не-браузерних середовищ, але вважається свого роду застарілим.
  • Пізніше, у 2022 році, було офіційно дозволено використовувати