Как мы помним, новые объекты могут быть созданы с помощью функции-конструктора 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 F
F.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"
.