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

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

Давайте начнем заново со следующих моментов:

  1. Все в Javascript - это объект (кроме примитивных типов)
  2. У каждого объекта есть прототип
  3. Прототип объекта по умолчанию - Object.prototype
  4. При обращении к свойству объекта объект смотрит на свои собственные свойства. Если не найден, он проверяет свойства своего прототипа. И это продолжается до тех пор, пока свойство не будет найдено или не достигнет Object.prototype. Если свойство не найдено в Object.prototype, возвращается undefined.

Если вам это непонятно, вы можете ознакомиться с документацией MDN для прототипов в Javascript.

  1. Что такое __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 конструктораPersonconstructor.

Оказывается, вложенное __proto__ свойство на самом деле указывает наObject.prototype.

Схема выглядит примерно так,

А давайте проверим, верна ли диаграмма.

Но какова цель свойства prototype в конструкторе Person?

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

Это может не иметь большого смысла.

Посмотрим на реальных примерах.

Давайте создадим новый объект с именем mySelf с помощью конструктора Person.

Обратите внимание, как конструктор заставляет свойство __proto__ объекта mySelf указывать на Person.prototype.

Окончательная иллюстрация выглядит примерно так:

Следовательно, единственная цель свойства prorotype функции-конструктора - инициализировать свойство __proto__ объектов, экземпляры которых создаются с помощью этого конструктора.

Заключение

Надеюсь, я запутал вас не больше, чем вы уже были. А теперь вы немного больше понимаете разницу между __proto__ и prototype.

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

Рад, что вы дочитали статью. Буду очень признателен за пару слов о вашей мысли. ✏️