Свойство F.prototype
и создание объектов через new
Объекты, помимо литеральной формы, создаются и функцией-конструктором черезnew
. Посмотрим, как указать прототип в этом случае.
У всех функций по умолчанию есть публичное, неперечислимое свойство, называемое prototype
, которое указывает на некий объект.
function Foo() {}
Foo.prototype; // { }
У каждого объекта, создаваемого с помощью вызова new Foo()
, ссылка [[Prototype]]
будет указывать на этот объект "Foo точка prototype"
.
function Foo() { // ... }
var a = new Foo();
Object.getPrototypeOf( a ) === Foo.prototype; // true
Вызов new Foo()
создает новый объект (мы назвали его a
), и этот новый объект a
связан внутренней ссылкой [[Prototype]]
с объектом Foo.prototype
.
Механика
function Foo(name) {
this.name = name;
}
Foo.prototype.myName = function() {
return this.name;
};
var a = new Foo( "a" );
var b = new Foo( "b" );
a.myName(); // "a"
b.myName(); // "b"
Этот пример показывает два дополнительных трюка для "класс-ориентированности":
this.name = name
: свойство.name
добавляется в каждый объект (a
иb
, соответственно; привязкаthis
), аналогично тому как экземпляры классов инкапсулируют значения данных.Foo.prototype.myName = ...
: возможно более интересный прием, добавляет свойство (функцию) в объектFoo.prototype
. Теперь работаетa.myName()
, но каким образом?
В примере выше велик соблазн думать, что при созданииa
иb
свойства/функции объектаFoo.prototype
копируются в каждый из объектовa
иb
. Однако этого не происходит.
В начале этой главы мы изучали ссылку[[Prototype]]
— часть стандартного алгоритма[[Get]]
, которая предоставляет запасной вариант поиска, если ссылка на свойство отсутствует в самом объекте.
В силу того, как создаютсяa
иb
, оба объекта получают внутреннюю ссылку[[Prototype]]
наFoo.prototype
. КогдаmyName
не находится вa
илиb
соответственно, она обнаруживается вFoo.prototype
.
Еще пример:
var animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
var rabbit = new Rabbit("Кроль"); // rabbit.__proto__ == animal
console.log( rabbit.eats ); // true
Установка Rabbit.prototype = animal
буквально говорит интерпретатору следующее: "При создании объекта через new Rabbit
запиши ему __proto__ = animal
"
P.S:
В результате получилось два объекта (любой пример выше), связанных друг с другом. Вот и все. Мы не создали экземпляр класса. И мы уж точно не копировали никакого поведения из "класса" в реальный объект. Мы просто связали два объекта друг с другом.
На самом деле секрет, о котором не догадывается большинство JS разработчиков, состоит в том, что вызов функции new Foo()
практически никак напрямую не связан с процессом создания ссылки. Это всегда было неким побочным эффектом. new Foo()
— это косвенный, окольный путь к желаемому результату: новому объекту, связанному с другим объектом. Можем ли мы добиться желаемого более прямым путем? Да! Герой дня — Object.create(..)
. Но мы вернемся к нему чуть позже.
В JavaScript мы не делаем копии из одного объекта ("класса") в другой ("экземпляр"). Мы создаем ссылки между объектами.