Прототипы в JavaScript — подробно с примерами

В этом руководстве вы узнаете о прототипе JavaScript и о том, как он работает внутри.

В JavaScript есть встроенная функция Object(). Оператор typeof возвращает 'function' если вы передаете ему функцию Object. Например:

typeof(Object)

Выход:

'function'

Обратите внимание, что Object() — это функция, а не объект. Это сбивает с толку, если вы впервые узнали о прототипе JavaScript.

Кроме того, JavaScript предоставляет анонимный объект, на который можно сослаться через свойство prototype функции Object() :

console.log(Object.prototype);

Прототип JavaScript

Объект Object.prototype имеет некоторые полезные свойства и методы, такие как toString() и valueOf().

Обратите внимание, что когда функция является значением свойства объекта, она называется методом. Следовательно, метод — это свойство объекта со значением в виде функции.

Object.prototype также имеет важное свойство, называемое constructor, которое ссылается на функцию Object().

Следующий оператор подтверждает, что свойство Object.prototype.constructor ссылается на функцию Object :

console.log(Object.prototype.constructor === Object); // true

Предположим, круг представляет собой функцию, а квадрат представляет собой объект. На следующем рисунке показана связь между функцией Object() и объектом Object.prototype :

Связь между функцией Object() и объектом Object.prototype

Во-первых, определите функцию-конструктор с именем Person следующим образом:

function Person(name) {
    this.name = name;
}

В этом примере функция Person() принимает аргумент name и присваивает его свойству name объекта this.

JavaScript создает новую функцию Person() и анонимный объект:

Прототип JS - тип человека

Как и функция Object(), функция Person() имеет свойство, называемое prototype, которое ссылается на анонимный объект. И анонимный объект имеет свойство constructor, которое ссылается на функцию Person().

Ниже показана функция Person() и анонимный объект, на который ссылается Person.prototype :

console.log(Person); 
console.log(Person.prototype);

Функция Person() и анонимный объект

Кроме того, JavaScript связывает объект Person.prototype с объектом Object.prototype через [[Prototype]], что известно как связь с прототипом.

Связь с прототипом обозначена [[Prototype]] на следующем рисунке:

Связь с прототипом

Определение методов

Следующий пример определяет новый метод, называемыйgreet(), в объекте Person.prototype :

Person.prototype.greet = function() {
    return "Hi, I'm " + this.name + "!";
}

В этом случае движок JavaScript добавляет метод greet() к объекту Person.prototype :

Метод greet()

Следующий пример создает новый экземпляр Person :

let p1 = new Person('John');

Внутри движок JavaScript создает новый объект с именем p1 и связывает объект p1 с объектом Person.prototype через связь с прототипом:

Новый экземпляр Person

Связь между 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().

Поиск метода 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');

Создание еще одного экземпляра Person

Объект p2 имеет те же свойства и методы, что и объект p1.

В заключение, когда вы определяете метод для объекта- prototype, этот метод используется всеми экземплярами.

Определение методов в отдельном объекте

Следующий пример определяет метод draw() для объекта p1.

p1.draw = function() {
    return "I can draw.";
};

Движок JavaScript добавляет метод draw() к объекту p1, а не к объекту Person.prototype :

JS прототип - объект с методом

Это означает, что вы можете вызвать метод 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__.
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript