В этом руководстве вы узнаете о прототипе 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__.
