В этом руководстве вы узнаете о прототипе JavaScript и о том, как он работает внутри.
В JavaScript есть встроенная функция Object()
. Оператор typeof
возвращает 'function'
если вы передаете ему функцию Object
. Например:
typeof(Object)
Выход:
'function'
Обратите внимание, что Object()
— это функция, а не объект. Это сбивает с толку, если вы впервые узнали о прототипе JavaScript.
Кроме того, JavaScript предоставляет анонимный объект, на который можно сослаться через свойство prototype
функции Object()
:
console.log(Object.prototype);
Объект Object.prototype
имеет некоторые полезные свойства и методы, такие как toString()
и valueOf()
.
Обратите внимание, что когда функция является значением свойства объекта, она называется методом. Следовательно, метод — это свойство объекта со значением в виде функции.
Object.prototype
также имеет важное свойство, называемое constructor
, которое ссылается на функцию Object()
.
Следующий оператор подтверждает, что свойство Object.prototype.constructor
ссылается на функцию Object
:
console.log(Object.prototype.constructor === Object); // true
Предположим, круг представляет собой функцию, а квадрат представляет собой объект. На следующем рисунке показана связь между функцией Object()
и объектом Object.prototype
:
Во-первых, определите функцию-конструктор с именем Person
следующим образом:
function Person(name) { this.name = name; }
В этом примере функция Person()
принимает аргумент name
и присваивает его свойству name
объекта this
.
JavaScript создает новую функцию Person()
и анонимный объект:
Как и функция Object()
, функция Person()
имеет свойство, называемое prototype
, которое ссылается на анонимный объект. И анонимный объект имеет свойство constructor
, которое ссылается на функцию Person()
.
Ниже показана функция Person()
и анонимный объект, на который ссылается Person.prototype
:
console.log(Person); console.log(Person.prototype);
Кроме того, JavaScript связывает объект Person.prototype
с объектом Object.prototype
через [[Prototype]]
, что известно как связь с прототипом.
Связь с прототипом обозначена [[Prototype]]
на следующем рисунке:
Определение методов
Следующий пример определяет новый метод, называемыйgreet()
, в объекте Person.prototype
:
Person.prototype.greet = function() { return "Hi, I'm " + this.name + "!"; }
В этом случае движок JavaScript добавляет метод greet()
к объекту Person.prototype
:
Следующий пример создает новый экземпляр Person
:
let p1 = new Person('John');
Внутри движок JavaScript создает новый объект с именем p1
и связывает объект p1
с объектом Person.prototype
через связь с прототипом:
Связь между p1
, Person.prototype
и Object.protoype
называется цепочкой прототипов.
Следующий код вызывает метод greet()
для объекта p1
:
let greeting = p1.greet(); console.log(greeting);
Поскольку p1
не имеет метода greet()
, JavaScript следует привязке к прототипу и находит его в объекте Person.prototype
.
JavaScript может найти метод greet()
в объекте Person.prototype
, он выполняет метод greet()
и возвращает результат:
Вызываем метод toString()
для объекта p1:
let s = p1.toString(); console.log(s);
В этом случае движок JavaScript следует цепочке прототипов, чтобы найти метод toString()
в Person.prototype
.
Поскольку у Person.prototype
нет метода toString()
, механизм JavaScript переходит к цепочке прототипов и ищет метод toString()
в объекте Object.prototype
.
JavaScript может найти метод toString()
в Object.prototype
, для этого выполняет метод toString()
.
Если вы вызываете метод, который не существует в Person.prototype
и Object.prototype
, механизм JavaScript будет следовать цепочке прототипов и выдаст ошибку, если не сможет найти метод. Например:
p1.fly();
Поскольку метод fly()
не существует ни для одного объекта в цепочке прототипов, движок JavaScript выдает следующую ошибку:
TypeError: p1.fly is not a function
Следующий пример создает еще один экземпляр Person
, чье свойство name равно 'Jane'
:
let p2 = new Person('Jane');
Объект p2
имеет те же свойства и методы, что и объект p1
.
В заключение, когда вы определяете метод для объекта- prototype
, этот метод используется всеми экземплярами.
Определение методов в отдельном объекте
Следующий пример определяет метод draw()
для объекта p1
.
p1.draw = function() { return "I can draw."; };
Движок JavaScript добавляет метод draw()
к объекту p1
, а не к объекту Person.prototype
:
Это означает, что вы можете вызвать метод draw()
для объекта p1
:
p1.draw();
Но вы не можете вызвать метод draw()
для объекта p2
:
p2.draw()
Ошибка:
TypeError: p2.draw is not a function
Когда вы определяете метод в объекте, этот метод доступен только для этого объекта. По умолчанию его нельзя использовать совместно с другими объектами.
Получение связи
__proto__
произносится как dunder proto. __proto__
— это свойство доступа объекта Object.prototype
. Оно раскрывает внутреннюю связь прототипа( [[Prototype]])
объекта, через который к нему осуществляется доступ.
Свойство __proto__
было стандартизировано в ES6 для обеспечения совместимости с веб-браузерами. Однако в будущем оно может быть Object.getPrototypeOf()
. Поэтому вы никогда не должны использовать __proto__
в своем производственном коде.
p1.__proto__
предоставляет [[Prototype]]
, который ссылается на объект Person.prototype
.
Точно так же p2.__proto__
также ссылается на тот же объект, что и p1.__proto__:
console.log(p1.__proto__ === Person.prototype); // true console.log(p1.__proto__ === p2.__proto__); // true
Как упоминалось ранее, вы должны использовать метод Object.getPrototypeOf()
вместо __proto__
.
Метод Object.getPrototypeOf()
возвращает прототип указанного объекта.
console.log(p1.__proto__ === Object.getPrototypeOf(p1)); // true
Другой популярный способ получить привязку к прототипу — это когда метод Object.getPrototypeOf()
недоступен, через свойство constructor
следующим образом:
p1.constructor.prototype
p1.constructor
возвращает Person
, поэтому p1.constructor.prototype
возвращает объект-прототип.
Затенение
Следующий вызов метода:
console.log(p1.greet());
В объекте p1
не определен метод greet()
, поэтому JavaScript обращается к цепочке прототипов, чтобы найти его. В этом случае он может найти метод в объекте Person.prototype
.
Добавим к объекту p1
новый метод с тем же именем, что и у метода в объекте Person.prototype
:
p1.greet = function() { console.log('Hello'); }
И вызовем метод greet()
:
console.log(p1.greet());
Поскольку объект p1
имеет метод greet()
, JavaScript просто выполняет его немедленно, не просматривая цепочку прототипов.
Это пример затенения. Метод greet()
объекта p1
заменяет метод greet()
объекта- prototype
, на который ссылается объект p1
.
Заключение
- Функция
Object()
имеет свойство, называемоеprototype
, которое ссылается на объектObject.prototype
. - Объект
Object.prototype
имеет все свойства и методы, которые доступны во всех объектах, таких какtoString()
иvalueOf()
. - Объект
Object.prototype
имеет свойствоconstructor
, которое ссылается на функциюObject
. - У каждой функции есть объект-
prototype
. Этот объект-прототип ссылается на объектObject.prototype
через связь[[prototype]]
или свойство__proto__
. - Цепочка прототипов позволяет одному объекту использовать методы и свойства своих объектов-
prototype
через связи[[prototype]]
. - Метод
Object.getPrototypeOf()
возвращает объект-прототип данного объекта. Используйте методObject.getPrototypeOf()
вместо__proto__
.