Введение в прототипы JavaScript

Объектно-ориентированный JavaScript

Хотя JavaScript не является языком, основанным на классах, и не реализует объектно-ориентированное программирование в традиционном смысле, он предоставляет возможности и шаблоны, которые позволяют использовать концепции объектно-ориентированного программирования. Это можно назвать прототипным наследованием.

Что такое прототип в JavaScript?

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

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

Разберем два прототипа: свойство prototype и сам прототип объекта. Начнем со свойства prototype.

Свойство prototype

Когда мы используем методы JavaScript, такие как pop в отношении массива или include при работе со строками, мы применяем встроенные методы JavaScript. Рассмотрим это на примере. Начнем с простого доступа к свойству prototype в конструкторе прототипов Array.

Теперь создадим массив и посмотрим, как можно просматривать свойство prototype внутри массива.

В приведенном выше примере переменная ourArray объявляется и инициализируется массивом, содержащим значения 1, 2 и 3. Позже нужно будет рассмотреть массив ourArray. Чтобы удалить элемент из конца массива, можно использовать метод pop. Но откуда он берется? Взглянем на массив ourArray еще раз.

Каждый объект в JavaScript имеет свойство [[Prototype]]. Это внутреннее свойство, которое обозначается двойными скобками, в которые оно заключено. Выбрав свойство [[Prototype]], мы видим доступные методы, которые в данном примере наследуются от конструктора Array. Мы также можем использовать метод getPrototypeOf, который вернет свойство [[Prototype]] данного объекта (помните, что массивы  —  это особый тип объектов).

Вы также можете встретить примеры, в которых используется свойство __proto__. Это унаследованная функция, и в eslint есть правило (no-proto), защищающее от ее использования. Свойство __proto__ раскрывает внутренний [[Prototype]] объекта. Лучше использовать getPrototypeOf, как показано ниже.

Мы можем провести сравнение с методами, перечисленными на сайте mdn. Итак, все массивы могут получить доступ к этим методам, но сами они не обладают ими в качестве свойств.

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

Как это возможно?

Объект, который является значением свойства [[prototype]],  —  это прототип для того объекта, который мы рассматриваем. Если он не существует, его значение будет равно null. Когда мы используем метод pop на ourArray, интерпретатор JavaScript сначала будет искать этот метод в ourArray. Если он не найдет его там, то будет искать в прототипе.

Этот процесс называется наследованием прототипа или цепочкой прототипов. Его можно продолжать до бесконечности, но лучше не создавать сложную цепочку, потому что отладка может привести к путанице. Использование цепочки прототипов помогает хранить функцию только в одном объекте, и интерпретатор будет искать ее именно в нем, если не найдет в первом объекте. Возьмем для примера код.

function GameTracker(name, result) {
this.name = name;
this.result = result;
}

В приведенном выше коде мы объявляем функцию-конструктор под названием GameTracker с параметрами name и result. Если эта функция хранится в памяти, она хранится как функция с объектом.

У объекта есть свойство prototype, которое представляет собой пустой объект. Любые функции, которые создаются с помощью конструктора GameTracker, будут иметь доступ к свойству prototype. С его помощью внутри функции мы устанавливаем параметры name и result, чтобы они ссылались на текущий экземпляр объекта.

let playerOne = new GameTracker("Fred", 8);

Далее мы создаем переменную playerOne, изначально неинициализированную. Затем с помощью ключевого слова new создается новый экземпляр GameTracker. Когда мы используем ключевое слово new, устанавливается свойство prototype, которое будет ссылкой на объект GameTracker. Если мы рассмотрим переменную playerOne, то увидим следующее:

console.log(playerOne);
//Возвращает ---> GameTracker {name: 'Fred', result: 8}

Теперь попробуем определиться с конструктором playerOne:

Object.getPrototypeOf(playerOne);
//Возвращает ---> constructor: ƒ GameTracker(name, result)

Используя прототип, можно добавить метод к прототипу. Добавим метод для запуска игры:

GameTracker.prototype.start = function() {
return `Hello ${this.name} the game is ready.`;
}

Теперь снова посмотрим на объект playerOne. Мы видим, что функция start хранится в прототипе.

Мы можем использовать функцию start для playerOne. Попробуем это сделать:

playerOne.start();
//Возвращает ---> 'Hello Fred the game is ready.'

Готово!

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Codecupdev: Introducing Prototypes in JavaScript

Предыдущая статья3 худших совета по осваиванию науки о данных
Следующая статьяКак легко и быстро создать веб-приложение на базе МО с помощью Python