Подробно изучите прототипы, цепочку прототипов и прототипы dunder.
Прежде чем начать понимать, что такое прототипы, вам нужно знать, как работает new
ключевое слово. Это связано с тем, что ключевое слово new
считается вызовом конструктора, но на самом деле оно служит гораздо более глубокой цели, которая ведет к прототипу, цепочке прототипов, прототипу dunder и ключевому слову this
.
Все объекты в нашем коде создаются с помощью «вызова конструктора» (через new
). В процессе создания эти объекты создаются «на основе» прототипа их конструктора. Вызов конструктора создает объект на основе собственного прототипа. Часть «на основе» — это 💀 неправильное направление.
То, что это вызов конструктора, не означает, что конструктор существует. Это из-за отсутствия лучшего термина.
Теория классов
Это означает, что классово-ориентированное кодирование по своей сути является операцией копирования. В настоящее время не существует конкретного стандарта того, что на самом деле представляет собой шаблон проектирования классов, но среди программистов преобладают способы Java/C++ (традиционные языки, ориентированные на классы). Это создает ожидание того, как должен работать классово-ориентированный язык, копирование.
Когда вы берете чертеж здания и создаете здание, вернитесь к чертежу и внесите в него изменения. Повлияет ли это на здание, которое вы только что создали?
Нет, потому что связь между классом и экземпляром существовала «долю секунды», характеристики класса были скопированы, после чего связь перестает существовать. Наследование по своей сути также является операцией копирования.
Поэтому, когда мы говорим — «вызов конструктора» создает объект «на основе» его собственного прототипа, мы говорим, что конструктор создает копию прототипа в экземпляре. За исключением того, что JavaScript вообще не копирует. Это не «основано на», это на самом деле «связано с».
«Вызов конструктора» связывает объект со своим собственным прототипом.
Связывание и копирование — принципиально разные операции. Из-за этого принципиально другого способа работы, т. е. связывания, JavaScript ведет себя не так, как языки программирования, такие как Java/C++.
Прототип класса
Традиционный способ создания «классов» в JavaScript. Это называется прототипным классом.
function Comment(author) { this.author = author; } Comment.prototype.makeComment = function (message) { console.log(this.author + " - " + message); }; var comment1 = new Comment("James"); var comment2 = new Comment("Bond"); comment1.makeComment("I love this!"); // James - I love this! comment2.makeComment("What's this?"); // Bond - What's this?
Это то, что делает для нас ключевое слово class
, и это то, что мы в основном используем в современном JavaScript. Поскольку это не операция копирования, важно знать, как работают классы и где находятся все связи.
Сеть прототипов
Мы рассмотрим следующий пример кода, чтобы понять цепочку прототипов. Кроме того, чтобы понять, мы будем использовать диаграммы, где:
- ⬛️ квадраты представляют объекты
- ⚫️ кружки обозначают функции
Строка 0
Еще до того, как наш код запустится, у нас есть функция с именем Object
. Это один из фундаментальных и наиболее важных объектов в JavaScript. Он также имеет несколько утилит, таких как Object.values
&Object.keys
. Таким образом, он также служит пространством имен для других методов.
Здесь также существует объект, самый важный объект во всем JavaScript. У нас нет для него имени, поэтому он назван в соответствии с тем, что на него указывает. Свойство Object
, указывающее на эту вещь, называется прототип, поэтому мы называем этот важный объект Object.prototype
.
В этом Object.prototype
есть некоторые важные вещи, такие как toString
, valueOf
и все эти другие фундаментальные утилиты, которые существуют в JavaScript.
Все непримитивы происходят непосредственно от этого
Object.prototype
Также есть еще одно свойство, указывающее на Object
с именем конструктор. Это имя не имеет ничего общего с системой классов.
Линия 1
Здесь у нас есть функция Comment
, а также объект, созданный благодаря функции Comment
. На данный момент он пуст, но позже он будет служить важной цели. Мы ссылаемся на этот объект как Comment.prototype
. Кроме того, есть свойство, указывающее назад, и оно называется конструктором.
Существует скрытая связь между объектом, «созданным функцией Comment
» и «объектом, созданным функцией Object
». Мы обсудим это в статье.
Строка 5
Здесь мы добавили свойство info
(неважно, функция это или нет) в файл Comment.prototype
.
Переход к строке 9.
Строка 9
Здесь мы используем ключевое слово new
для вызова функции Comment
. Когда мы используем ключевое слово new
, происходят 4 вещи:
- Он создает пустой объект
- затем он связывает этот объект с другим объектом
- Затем функция (в данном случае
Comment
) сthis
указывает на объект, созданный ключевым словомnew
- Наконец, если функция ничего не возвращает, ключевое слово
new
возвращает только что созданный объект.
Прочтите статью о ключевом слове new
, чтобы подробно узнать о его работе.
Итак, в этом случае ключевое слово new
создало объект и связало его с Workshop.prototype
. Затем он вызывает функцию Comment
, где this
указывает на вновь созданный объект, а teacher
добавляется к этому новому объекту со значением I love this!
(это происходит в строке 2).
Аналогично в этом случае со строкой 10.
Строка 12
Здесь, когда мы запускаем makeComment
для объекта comment1
, мы не получаем ошибки, хотя comment1
не имеет свойства makeComment
. Почему? Поскольку теперь существует внутренняя скрытая связь, называемая цепочкой прототипов, между comment1
и Workshop.prototype
, всякий раз, когда мы ищем свойство, а оно не существует, по умолчанию JavaScript перемещается на один уровень вверх в этой цепочке прототипов к посмотрите, есть ли он у этого объекта ИЛИ нет.
В этом случае мы перейдем от comment1
к Workshop.prototype
и проверим наличие makeComment
. Поскольку он там существует, мы можем использовать makeComment
на объекте comment1
. Здесь this
внутри функции makeComment
указывает на объект comment1
, потому что контекст для this
зависит от его реального места вызова, которым здесь является строка 12.
Прочитайте статью о this
ключевом слове, чтобы узнать больше о его работе.
Заключение
Мы можем совместно использовать один метод info
с несколькими экземплярами, и все они могут совместно использовать его из-за this
поведения привязки и связи с прототипом. Именно это делает прототипы в JavaScript очень мощными. По этой причине не рассматривайте JavaScript с точки зрения традиционных языков программирования.
Дандер Прототип
Функция Comment
не имеет ничего общего с созданием comment1
. Именно ключевое слово new
создало объект comment1
. Когда вы сделаете comment1.constructor == Comment
, вы получите true
. Это просто для того, чтобы сказать, что Comment
был создан comment1
(что не соответствует действительности).
Всякий раз, когда есть что-то вроде __something__
, это называется Dunder. Поэтому __proto__
называется dunder proto, а comment1.__proto__
будет называться 🗣 «comment 1 dunder proto».
function Comment(author) { this.author = author; } Comment.prototype.makeComment = function (message) { console.log(this.author + " - " + message); }; var comment1 = new Comment("James"); // Prototype Linkage/Chain console.log(comment1.constructor === Comment); // true console.log(comment1.__proto__ === Comment.prototype); // true console.log(Object.getPrototypeOf(comment1) === Comment.prototype); // true
Что такое (comment1.__proto__ == Comment.prototype) = true
? Это означает, что __proto__
в объекте относится к объекту, с которым он связан в цепочке прототипов.
Здесь comment1
не имеет такого свойства, как __proto__
, поэтому мы поднимаемся на один уровень вверх по цепочке прототипов к Comment.prototype
. Comment.prototype
также не имеет __proto__
. Итак, теперь мы поднимаемся на один уровень вверх по цепочке прототипов, снова к Object.prototype
.
В Object.prototype
у нас есть __proto__
, это не свойство, а функция получения/установки. comment1.__proto__
обращается к нему как к свойству, но когда к нему обращаются, вызывается функция, и ключевое слово this
в нем будет указывать на объект comment1
, так как это место вызова.