Як відомо, ми можемо створювати нові об’єкти за допомогою функції-конструктора, ось так new F()
.
Якщо в F.prototype
міститься об’єкт, то оператор new
автоматично встановлює той об’єкт в [[Prototype]]
для новоствореного об’єкта.
JavaScript використовувала прототипне наслідування з моменту своєї появи. Це одна з особливостей цієї мови.
Але раніше, в давні часи, прямого доступу до прототипа в об’єкта не було. Надійно працювала лише властивість “prototype” функції-конструктора, описане в цьому розділі. Тому ця властивість використовується в багатьох скриптах.
Зауважте, що F.prototype
тут є звичайною властивістю, яку назвали "prototype"
в об’єкті F
. Це звучить дуже співзвучно з терміном “prototype”, але тут це означає всього лиш звичайне ім’я властивості.
Ось приклад:
let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("Білий кролик"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
Вираз Rabbit.prototype = animal
дослівно означає наступне: “коли буде створено new Rabbit
, його властивість [[Prototype]]
має посилатись на об’єкт animal
”.
Ось кінцева картинка:
На картинці, "prototype"
що зображена біля горизонтальної стрілки – це звичайна властивість, а [[Prototype]]
, що зображена біля вертикальної стрілки – вказує на те, що rabbit
успадковує властивості від свого прототипа animal
.
F.prototype
використовується тільки у в момент виклику функції-конструктора new F
F.prototype
використовується тільки при виклику new F
і присвоюється в якості властивості [[Prototype]]
нового об’єкта.
Якщо після створення властивість F.prototype
зміниться (F.prototype = <інший об'єкт>
), то нові об’єкти, створені з допомогою new F
, будуть мати в якості [[Prototype]]
інший об’єкт, а вже існуючі об’єкти будуть посилатись на старий.
Типове значення F.prototype, властивості конструктора
Кожна функція за замовчуванням має властивість "prototype"
, навіть якщо ми цю властивість самі не прописуємо.
За замовчуванням “prototype” є об’єктом з однією єдиною властивістю constructor
, яка посилається на саму ж функцію.
Ось як тут:
function Rabbit() {}
/* значення prototype, яке призначається за замовченням
Rabbit.prototype = { constructor: Rabbit };
*/
Можемо перевірити це:
function Rabbit() {}
// за замовчуванням:
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
Ну й логічно, якщо ми самі нічого з властивістю constructor
не робимо, то вона є доступна для всіх об’єктів rabbit
через [[Prototype]]
:
function Rabbit() {}
// за замовчуванням:
// Rabbit.prototype = { constructor: Rabbit }
let rabbit = new Rabbit(); // успадковує від {constructor: Rabbit}
alert(rabbit.constructor == Rabbit); // true (від прототипу)
Ми можемо використовувати властивість constructor
для створення нових об’єктів використовуючи ту саму функцію-конструктор, як і для вже існуючих.
Як тут:
function Rabbit(name) {
this.name = name;
alert(name);
}
let rabbit = new Rabbit("Білий кролик");
let rabbit2 = new rabbit.constructor("Чорний кролик");
Це дуже практично у випадку якщо ми маємо об’єкт, не знаємо за допомогою якого саме конструктора той об’єкт був створений (до прикладу якщо він був імпортований з якоїсь бібліотеки), а нам потрібно створити новий об’єкт по типу того, що вже існує.
Але найважливішим моментом щодо "constructor"
є те, що…
…Сама мова JavaScript не забезпечує правильного значення "constructor"
.
Так, constructor
існує за замовчуванням у властивостях "prototype"
для функцій, але це й усе. Подальша доля "constructor"
повністю в наших руках.
А саме, якщо ми замінимо дефолтне значення prototype
на якесь інше, тоді в prototype
не буде ніякого "constructor"
.
Наприклад:
function Rabbit() {}
Rabbit.prototype = {
jumps: true
};
let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false
Отже, щоб мати правильний "constructor"
, замість цілком заміняти дефолтний prototype
якимось нашим іншим об’єктом, ми можемо просто додавати або видаляти властивості до уже наявного дефолтного "prototype"
:
function Rabbit() {}
// Тут ми не заміняємо цілковито властивість Rabbit.prototype
// а просто додаємо до неї
Rabbit.prototype.jumps = true
// а тому дефолтне Rabbit.prototype.constructor зберігається
чи ось інший варіант, відновлюємо constructor
вручну:
Rabbit.prototype = {
jumps: true,
constructor: Rabbit
};
// і тепер, constructor також правильний, тому що ми додали його вручну
Підсумок
В цьому розділі було коротко описано шлях для встановлення прихованої властивості [[Prototype]]
об’єктів, які були створені за допомогою функції-конструктора. Пізніше, буде надано більше прикладів коду, які покладаються на ці властивості.
Все досить просто, тільки треба додати кілька деталей щоб усе було зрозуміло:
- Властивість об’єкта
F.prototype
(ні в якому разі не[[Prototype]]
) встановлює приховану властивість[[Prototype]]
нового об’єкта, тільки тоді, коли буде викликана черезnew F()
. - Значення властивості
F.prototype
може бути або посиланням на об’єкт, абоnull
: інші значення не спрацюють. - Тільки властивість
"prototype"
має такий спеціальний ефект: може встановлюватись в конструкторі та може викликатись через операторnew
.
У звичайних об’єктах властивість prototype
не є чимось спеціальним:
let user = {
name: "John",
prototype: "Bla-bla" // немає ніякої магії
};
За замовчуванням, усі функції мають F.prototype = { constructor: F }
, і ми можемо отримати конструктор об’єкта через його властивість "constructor"
.
Коментарі
<code>
, для кількох рядків – обгорніть їх тегом<pre>
, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)