Javascript сбивает с толку. И среди всех загадочных вещей разница между __proto__
и prototype
выделяется однозначно. Это сбивает с толку большинство программистов, которые пытаются изучить объектно-ориентированный аспект языка.
Эта статья предназначена для разработчиков, которые имеют базовые представления об объектно-ориентированном программировании в целом и изучают способ, которым это делает Javascript.
Давайте начнем заново со следующих моментов:
- Все в Javascript - это объект (кроме примитивных типов)
- У каждого объекта есть прототип
- Прототип объекта по умолчанию -
Object.prototype
- При обращении к свойству объекта объект смотрит на свои собственные свойства. Если не найден, он проверяет свойства своего прототипа. И это продолжается до тех пор, пока свойство не будет найдено или не достигнет
Object.prototype
. Если свойство не найдено вObject.prototype
, возвращается undefined.
Если вам это непонятно, вы можете ознакомиться с документацией MDN для прототипов в Javascript.
- Что такое
__proto__
и каково его назначение?
Чтобы лучше понять это, давайте продолжим несколько примеров.
person
- это простой объект с тремя свойствами: name
, age
и gender
.
При использовании console.dir
на этом person
объекте мы видим, что у него есть __proto__
property.
Свойство __proto__
- это свойство по умолчанию, добавляемое к каждому объекту. Это свойство указывает на prototype
объекта.
По умолчанию prototype
для каждого объекта - Object.prototype
. Следовательно, свойство __proto__
объекта person
указывает на Object.prototype
.
Если все, что я сказал, правда, то иллюстрация будет выглядеть примерно так.
Давайте проверим, что я утверждаю на самом деле.
Как видите, свойство __proto__
объекта person
действительно равноObject.prototype
.
Теперь я хочу создать еще один объект teacher
. Этот объект будет иметь те же свойства и значения, что и объект person
. Будет добавлено только одно дополнительное свойство -- subject
.
По умолчанию свойство teacher
__proto__
указывает на объект Object.prototype
.
И иллюстрация будет выглядеть примерно так.
Вы видите здесь проблему? teacher
и person
имеют три общих свойства с одинаковыми значениями. Было бы здорово, если бы мы могли каким-то образом наследовать свойства teacher
от person
.
Для этого сначала оставим текущий teacher
объект. И создайте новый только со свойством subject
, потому что мы унаследуем другие свойства от объекта person
.
Теперь структура выглядит примерно так.
Обратите внимание, что новое свойство teacher
__proto__
по-прежнему указывает на объектObject.prototype
. Чтобы наследовать, мы должны сделать так, чтобы свойство teacher
__proto__
указывало наperson
.
Мы можем сделать это с помощью функции Object.setPrototypeOf
.
Теперь иллюстрация выглядит примерно так.
Свойство teacher
__proto__
указывает на объект person
, а __proto__
property person
указывает на объект Object.prototype
.
Давайте проверим, действительно ли свойство teacher
__proto__
указывает на объект person
.
Мы успешно унаследовали свойства объекта person
через цепочку прототипов.
Теперь мы можем получить доступ к свойствам из объекта teacher
. Если свойство находится в самом объекте, оно вернет его. В противном случае он бы запросил свой прототип и так далее.
Следует отметить одну важную вещь: __proto__
- это просто ссылка на прототип, а не его экземпляр. Следовательно, если мы изменим какое-либо свойство в объекте-прототипе, это также повлияет на дочерние объекты.
Посмотрите, как изменение свойства person
также меняет свойство __proto__
teacher
.
Таким образом, можно с уверенностью сказать, что термин прототипное наследование не совсем точен. Это больше похоже на делегирование, чем на фактическое наследование!
В двух словах, свойство __proto__
каждого объекта указывает на прототип объекта.
2. Что такое свойство prototype
в функциях конструктора и каково его назначение?
Начнем с примеров, поскольку это помогает лучше понять тему.
Что, если бы нам пришлось создать группу из person
объектов с одинаковыми свойствами, но разными значениями?
Мы можем легко создать конструктор, отвечающий этой цели.
Конструктор - это особый тип функции. Это похоже на чертеж объекта. Вы можете использовать конструктор для создания объектов с одинаковыми свойствами, но разными значениями.
Конструктор будет выглядеть примерно так.
Поскольку функции также являются объектами, у него будет свойство __proto__
, которое будет указывать на прототип функции.
Функции - это особые типы объектов. Свойство функции __proto__
указывает наFunction.prototype
, а не на Object.prototype
.
Однако, помимо свойства __proto__
, функции-конструкторы также имеют свойство prototype
.
Обратите внимание на свойство __proto__
, вложенное в свойство prototype
конструктораPerson
constructor.
Оказывается, вложенное __proto__
свойство на самом деле указывает наObject.prototype
.
Схема выглядит примерно так,
А давайте проверим, верна ли диаграмма.
Но какова цель свойства prototype
в конструкторе Person
?
Оказывается, всякий раз, когда мы создаем экземпляр объекта с помощью конструктора Person
, конструктор заставляет свойство __proto__
нового объекта указывать на тот же объект, что и его свойство prototype
.
Это может не иметь большого смысла.
Посмотрим на реальных примерах.
Давайте создадим новый объект с именем mySelf
с помощью конструктора Person
.
Обратите внимание, как конструктор заставляет свойство __proto__
объекта mySelf
указывать на Person.prototype
.
Окончательная иллюстрация выглядит примерно так:
Следовательно, единственная цель свойства prorotype
функции-конструктора - инициализировать свойство __proto__
объектов, экземпляры которых создаются с помощью этого конструктора.
Заключение
Надеюсь, я запутал вас не больше, чем вы уже были. А теперь вы немного больше понимаете разницу между __proto__
и prototype
.
Несомненно, это плохой дизайн, который сбивает с толку многих программистов. Но, как вы знаете, Javascript - это больше вращающийся язык, чем хорошо спроектированный. Так что рано или поздно мы должны смириться с этим.
Рад, что вы дочитали статью. Буду очень признателен за пару слов о вашей мысли. ✏️