Как мы помним, новые объекты могут быть созданы с помощью функции-конструктора new F().
Если в F.prototype содержится объект, оператор new устанавливает его в качестве [[Prototype]] для нового объекта.
JavaScript использовал прототипное наследование с момента своего появления. Это одна из основных особенностей языка.
Но раньше, в старые времена, прямого доступа к прототипу объекта не было. Надёжно работало только свойство "prototype" функции-конструктора, описанное в этой главе. Поэтому оно используется во многих скриптах.
Обратите внимание, что F.prototype означает обычное свойство с именем "prototype" для F. Это ещё не «прототип объекта», а обычное свойство F с таким именем.
Приведём пример:
let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
Установка Rabbit.prototype = animal буквально говорит интерпретатору следующее: «При создании объекта через new Rabbit() запиши ему animal в [[Prototype]]».
Результат будет выглядеть так:
На изображении: "prototype" – горизонтальная стрелка, обозначающая обычное свойство для "F", а [[Prototype]] – вертикальная, обозначающая наследование rabbit от animal.
F.prototype используется только в момент вызова new FF.prototype используется только при вызове new F и присваивается в качестве свойства [[Prototype]] нового объекта.
Если после создания свойство F.prototype изменится (F.prototype = <другой объект>), то новые объекты, созданные с помощью new F, будут иметь в качестве [[Prototype]] другой объект, а уже существующие объекты сохранят старый.
F.prototype по умолчанию, свойство constructor
У каждой функции (за исключением стрелочных) по умолчанию уже есть свойство "prototype".
По умолчанию "prototype" – объект с единственным свойством constructor, которое ссылается на функцию-конструктор.
Вот такой:
function Rabbit() {}
/* прототип по умолчанию
Rabbit.prototype = { constructor: Rabbit };
*/
Проверим это:
function Rabbit() {}
// по умолчанию:
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
Соответственно, если мы ничего не меняем, то свойство constructor будет доступно всем кроликам через [[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("White Rabbit");
let rabbit2 = new rabbit.constructor("Black Rabbit");
Это удобно, когда у нас есть объект, но мы не знаем, какой конструктор использовался для его создания (например, он мог быть взят из сторонней библиотеки), а нам необходимо создать ещё один такой объект.
Но, пожалуй, самое важное о свойстве "constructor" это то, что…
…JavaScript сам по себе не гарантирует правильное значение свойства "constructor".
Да, оно является свойством по умолчанию в "prototype" у функций, но что случится с ним позже – зависит только от нас.
В частности, если мы заменим прототип по умолчанию на другой объект, то свойства "constructor" в нём не будет.
Например:
function Rabbit() {}
Rabbit.prototype = {
jumps: true
};
let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false
Таким образом, чтобы сохранить верное свойство "constructor", мы должны добавлять/удалять/изменять свойства у прототипа по умолчанию вместо того, чтобы перезаписывать его целиком:
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…)