Прототипы объектов в JavaScript

0 Акции
0
0
0
0

Введение

 

Прототипы — это механизм, с помощью которого объекты JavaScript наследуют свойства друг от друга. В этой статье мы объясним, что такое прототип, как работают цепочки прототипов и как установить прототип для объекта.

Предпосылки

Разберитесь в функциях JavaScript, ознакомьтесь с основами JavaScript (см. «Начало работы» и «Основные блоки») и принципами объектно-ориентированного программирования (см. «Введение в объекты»).

Прототип цепи

Попробуйте создать литеральный объект в консоли браузера:

const myObject = {
city: "Madrid",
greet() {
console.log(`Greetings from ${this.city}`);
},
};
myObject.greet(); // Greetings from Madrid

Это объект с одним свойством данных — city — и одним методом — greet(). Если вы введете в консоль имя объекта, а затем точку, например, myObject., консоль отобразит список всех доступных свойств этого объекта. Вы увидите, что помимо city и greeting есть гораздо больше свойств!

__defineGetter__
__defineSetter__
__lookupGetter__
__lookupSetter__
__proto__
city
constructor
greet
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocaleString
toString
valueOf

Попробуйте получить доступ к одному из них:

myObject.toString(); // "[object Object]"

Это работает (даже если не совсем понятно, что делает функция toString()).

Что это за дополнительные свойства и откуда они берутся?

В JavaScript каждый объект имеет встроенное свойство, называемое прототипом. Прототип сам по себе является объектом, поэтому у каждого прототипа будет свой собственный прототип, создавая так называемую цепочку прототипов. Эта цепочка обрывается, когда мы достигаем прототипа, у которого в качестве прототипа указано значение null.

Примечание: Свойство объекта, указывающее на его прототип, не называется прототипом. Это название не является стандартным, но на практике все браузеры используют __proto__. Стандартный способ доступа к прототипу объекта — это метод Object.getPrototypeOf().

 

При попытке доступа к свойству объекта: если свойство не найдено в самом объекте, выполняется поиск в прототипе свойства. Если свойство по-прежнему не найдено, выполняется поиск в прототипе экземпляра, и так далее, пока свойство не будет найдено или не будет достигнут конец цепочки, в этом случае возвращается значение undefined.

Таким образом, когда мы вызываем метод myObject.toString(), браузер:

  • Ищет toString в myObject
  • Я не могу найти его там, поэтому он ищет `toString` в прототипе объекта `myObject`.
  • Он находит его там и делает звонок.

Какой прототип у объекта myObject? Чтобы это выяснить, мы можем использовать функцию Object.getPrototypeOf():

Object.getPrototypeOf(myObject); // Object { }

Это объект с именем Object.prototype, представляющий собой самый базовый прототип, который есть у всех объектов по умолчанию. Прототип Object.prototype равен null, поэтому он находится в конце цепочки прототипов:

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes/myobject-prototype-chain.svg

Прототип объекта не всегда равен Object.prototype. Попробуйте следующее:

const myDate = new Date();
let object = myDate;
do {
object = Object.getPrototypeOf(object);
console.log(object);
} while (object);
// Date.prototype
// Object { }
// null

Этот код создает объект Date, затем перемещается вверх по цепочке прототипов и регистрирует прототипы. Он показывает нам, что прототипом объекта myDate является объект Date.prototype, а его прототипом — Object.prototype.

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes/mydate-prototype-chain.svg

Фактически, когда вы вызываете знакомые методы, такие как myDate2.getTime(), вы вызываете метод, определенный в Date.prototype.

свойства затенения

Что произойдет, если определить свойство объекта, если в прототипе этого же объекта уже определено свойство с тем же именем? Давайте посмотрим:

const myDate = new Date(1995, 11, 17);
console.log(myDate.getTime()); // 819129600000
myDate.getTime = function () {
console.log("something else!");
};
myDate.getTime(); // 'something else!'

Это должно быть предсказуемо, учитывая описание цепочки прототипов. Когда мы вызываем getTime(), браузер сначала ищет свойство с таким именем в myDate и проверяет прототип только в том случае, если myDate его не определяет. Поэтому, когда мы добавляем getTime() к myDate, вызывается версия из myDate.

Это называется “затенением” объекта недвижимости.

Создание прототипа

В JavaScript существует несколько способов задать прототип объекта, и здесь мы опишем два из них: Object.create() и конструктор.

Использование Object.create

Метод Object.create() создает новый объект и позволяет указать объект, который будет использоваться в качестве прототипа нового объекта.

Вот пример:

const personPrototype = {
greet() {
console.log("hello!");
},
};
const carl = Object.create(personPrototype);
carl.greet(); // hello!

Здесь мы создаём объект personPrototype, имеющий метод greet(). Затем мы используем Object.create() для создания нового объекта с personPrototype в качестве прототипа. Теперь мы можем вызвать greet() для нового объекта, и прототип предоставит его реализацию.

Использование конструктора

В JavaScript у всех функций есть свойство с именем prototype. Когда вы вызываете функцию в качестве конструктора, этому свойству присваивается прототип вновь созданного объекта (по соглашению, в свойстве с именем __proto__).

Таким образом, если мы зададим прототип конструктора, мы сможем гарантировать, что всем объектам, созданным с помощью этого конструктора, будет присвоен этот прототип:

const personPrototype = {
greet() {
console.log(`hello, my name is ${this.name}!`);
},
};
function Person(name) {
this.name = name;
}
Object.assign(Person.prototype, personPrototype);
// or
// Person.prototype.greet = personPrototype.greet;

Здесь мы творим:

  • Объект personPrototype, имеющий метод greet().
  • Функция-конструктор Person(), которая инициализирует имя создаваемого человека.

Затем мы помещаем методы, определенные в personPrototype, в свойство prototype функции Person, используя Object.assign.

После выполнения этого кода объекты, созданные с помощью Person.prototype(), получат в качестве своего прототипа объект, который автоматически будет содержать метод greet.

const reuben = new Person("Reuben");
reuben.greet(); // hello, my name is Reuben!

Это также объясняет, почему мы ранее говорили, что прототип объекта myDate называется Date.prototype: это свойство прототипа конструктора объекта Date.

Собственная собственность

Создаваемые нами объекты с помощью конструктора Person, описанного выше, имеют два свойства:

  • Свойство name, задаваемое в конструкторе, чтобы оно отображалось непосредственно в объектах Person.
  • Метод `greet()`, заданный в прототипе.

Часто встречается такая схема, когда методы определяются в прототипе, а свойства данных — в конструкторе. Причина в том, что методы обычно одинаковы для каждого создаваемого объекта, в то время как нам часто нужно, чтобы каждый объект имел своё уникальное значение для своих свойств данных (как в данном случае, у каждого человека разное имя).

Свойства, определенные непосредственно в объекте, например, имя в данном случае, называются собственными свойствами, и вы можете проверить, является ли свойство конкретным, используя статический метод Object.hasOwn():

const irma = new Person("Irma");
console.log(Object.hasOwn(irma, "name")); // true
console.log(Object.hasOwn(irma, "greet")); // false

Примечание: Здесь также можно использовать нестатический метод Object.hasOwnProperty(), но мы рекомендуем использовать Object.hasOwn(), если это возможно.

Прототипы и наследование

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

Они специально поддерживают один из вариантов наследования. Наследование — это особенность объектно-ориентированных языков программирования, которая позволяет программистам выразить идею о том, что некоторые объекты в системе являются более специализированными версиями других объектов.

Например, если мы моделируем школу, у нас могут быть учителя и ученики: они оба люди, поэтому у них есть общие свойства (например, у обоих есть имена), но каждый может добавлять дополнительные свойства (например, у учителей есть предмет, который они преподают), или они могут реализовывать одно и то же свойство разными способами. В объектно-ориентированной системе мы могли бы сказать, что и учителя, и ученики наследуют от класса People.

Вы можете увидеть, как в JavaScript, если объекты Professor и Student могут иметь прототипы Person, они могут наследовать общие свойства, одновременно добавляя и переопределяя свойства, которые должны отличаться.

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

Результат

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *


Вам также может понравиться
amazon-s3-cloud-storage

Что такое Amazon S3? Экспертный обзор облачного хранилища Amazon.

В современном мире технологий огромные объёмы данных генерируются и перемещаются в любой момент. Компаниям, разработчикам и пользователям нужна надёжная, масштабируемая и всегда доступная платформа. В связи с этим Amazon S3 (Simple Storage Service) — популярный сервис AWS, призванный упростить и сделать более безопасным хранение и управление данными.

Как импортировать и экспортировать базы данных в MySQL или MariaDB

Введение: Импорт и экспорт баз данных — распространённая задача при разработке программного обеспечения. Вы можете использовать данные дампа…

Как форматировать код с помощью Prettier в Visual Studio Code

Введение Форматирование кода всегда представляет собой сложную задачу, но современные инструменты разработчика делают ее возможной...

Как установить стек Linux, Apache, MariaDB, PHP (LAMP) на Debian 10

Введение Стек LAMP — это группа программного обеспечения с открытым исходным кодом, которое обычно устанавливается вместе для питания сервера…