Чем __proto__ отличается от конструктора.prototype?

function Gadget(name, color)
{
   this.name = name;
   this.color = color;
}

Gadget.prototype.rating = 3

var newtoy = new Gadget("webcam", "black")

newtoy.constructor.prototype.constructor.prototype.constructor.prototype 

Он всегда возвращает объект с рейтингом = 3.

Но если я сделаю следующее:

newtoy.__proto__.__proto__.__proto__

Цепочка заканчивается возвратом null.

Также в Internet Explorer, как мне проверить нулевое значение, если нет свойства __proto__?


person xdevel2000    schedule 16.03.2009    source источник
comment
Эта графическая диаграмма поможет вам понять разницу между прототипом и прототипом. . Вы можете проследить цепочку proto от объекта newtoy, и тогда вы поймете, почему 3-й Proto от newtoy равен нулю.   -  person bits    schedule 09.10.2012
comment
Также из диаграммы ясно, что newtoy.prototype не равно newtoy.constructor.prototype и, следовательно, newtoy.constructor.prototype не будет иметь свойства с именем rating. Точно так же newtoy.constructor.prototype.constructor.property также не будет иметь свойства с именем rating.   -  person bits    schedule 09.10.2012
comment
Опечатка в последнем комментарии: поэтому newtoy.constructor.prototype будет иметь свойство, называемое рейтингом. Точно так же newtoy.constructor.prototype.constructor.property также будет иметь свойство, называемое рейтингом.   -  person bits    schedule 20.03.2013
comment
См. также: __proto__ Против. prototype в JavaScript и Как работает JavaScript .prototype?   -  person Bergi    schedule 15.03.2014
comment
Этот инструмент анализирует большинство используемых вами структур JavaScript. во время выполнения :)   -  person Mauricio Poppe    schedule 17.03.2015
comment
@bits Какой инструмент вы использовали для создания этой диаграммы? ,   -  person Royi Namir    schedule 06.06.2015
comment
@RoyiNamir Я сделал диаграмму, используя свой собственный инструмент в NodeJS. Я сделал это несколько лет назад, чтобы помочь себе изучить все эти тонкости Javascript. В этом инструменте вы можете ввести некоторый (небольшой) код javascript и указать, какие объекты вас в первую очередь интересуют. А затем он сгенерирует диаграмму (используя GraphViz), насколько ему известно. С тех пор мне удалось преобразовать его из NodeJS в обычный Javascript, поэтому он работает в браузере без каких-либо зависимостей. Если есть интерес, я могу найти время, чтобы почистить его и загрузить на github.   -  person bits    schedule 07.06.2015
comment
@bits Я хотел бы увидеть версию JS (браузер) для этого. вы можете увидеть мой адрес электронной почты в профиле.   -  person Royi Namir    schedule 07.06.2015
comment
@Royi Namir Я загрузил jsViz на github. Вот демонстрационный сайт. Пожалуйста, не обращайте внимания на то, насколько неподдерживаемым (и грязным) является фактический код. Это супер старый проект, к которому я не притрагивался целую вечность.   -  person bits    schedule 11.06.2015
comment
Отвечает ли это на ваш вопрос? __proto__ VS. прототип на JavaScript   -  person 0x90    schedule 15.08.2020


Ответы (8)


Недавно я пытался разобраться в этом и, наконец, придумал эту «карту», ​​которая, я думаю, проливает полный свет на этот вопрос.

https://i.stack.imgur.com/KFzI3.png введите здесь описание изображения

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

Макет объекта JavaScript

Самым удивительным для меня было обнаружить, что Object.__proto__ указывает на Function.prototype, а не на Object.prototype, но я уверен, что на это есть веская причина :-)

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

Object.O1='';
Object.prototype.Op1='';

Function.F1 = '';
Function.prototype.Fp1 = '';

Cat = function(){};
Cat.C1 = '';
Cat.prototype.Cp1 = '';

mycat = new Cat();
o = {};

// EDITED: using console.dir now instead of console.log
console.dir(mycat);
console.dir(o);
person drodsou    schedule 28.06.2012
comment
@utsaina Очень круто. Ознакомьтесь с другим графическим представлением кода, опубликованного OP. И я думаю, что наши диаграммы совпадают с точки зрения технических деталей. - person bits; 09.10.2012
comment
Причина, по которой Object.__proto__ указывает на Function.prototype, заключается в том, что Object() сама по себе является нативной функцией, которая создает экземпляр пустого объекта. Следовательно, Object() — это функция. Вы обнаружите, что свойства __proto__ всех других основных нативных типов указывают на Function.prototype. Object, Function, String, Number и Array наследуют прототип функции. - person Swivel; 27.06.2013
comment
@drodsou, твоя вторая ссылка потрясающая. Проверьте это сейчас, пожалуйста ;) mollypages.org/misc/js.mp Отлично объяснение :D - person abhisekp; 01.07.2016
comment
@Swivel Следовательно, Object () - это функция - вы хотели сказать, что Object - это функция? без () - person Giorgi Moniava; 09.12.2016
comment
@GiorgiMoniava Верно. Object сама по себе является функцией; результат выполнения callable Object (т. е. возвращаемое значение запуска Object()) не является функцией. - person Swivel; 09.12.2016
comment
Спасибо, ваша карта очень полезна! Я создал суть, основанную на вашей семантике, чтобы показать 1. как ведут себя отношения, когда Cat наследует от Animal и 2. эквивалентность между классами ES2015/ES6 и объектами ES5: gist.github.com/philipstanislaus/ - person Philip Stanislaus; 28.06.2017
comment
typeof Объект возвращает функцию - person Enrique; 19.06.2019

constructor — это предопределенное свойство [[DontEnum]] объекта, на которое указывает свойство prototype объекта функции, и первоначально оно будет указывать на сам объект функции.

__proto__ эквивалентно внутреннему свойству [[Prototype]] объекта, т.е. его фактическому прототипу.

Когда вы создаете объект с помощью оператора new, его внутреннее свойство [[Prototype]] будет установлено на объект, на который указывает свойство prototype функции-конструктора.

Это означает, что .constructor будет оцениваться как .__proto__.constructor, то есть функция-конструктор, используемая для создания объекта, и, как мы узнали, свойство protoype этой функции использовалось для установки [[Prototype]] объекта.

Отсюда следует, что .constructor.prototype.constructor идентично .constructor (пока эти свойства не были перезаписаны); см. здесь для более подробного объяснения.

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

person Christoph    schedule 16.03.2009
comment
Эта ссылка «здесь» является золотым стандартом. Идите туда, если хотите полное описание. - person Ricalsin; 21.08.2012
comment
Хороший улов с цепочкой .constructor.prototype. Мне тоже было непонятно, пока я не увидел, что .constructor равно .__proto__.constructor. Это просто означает циклическое переключение между функцией конструктора и его прототипом. - person Johnny_D; 08.05.2013

Прототипное наследование в JavaScript основано на свойстве __proto__ в том смысле, что каждый объект наследует содержимое объекта, на который ссылается его свойство __proto__.

Свойство prototype является специальным только для объектов Function и только при использовании оператора new для вызова конструктора Function as. В этом случае __proto__ созданного объекта будет установлено на Function.prototype конструктора.

Это означает, что добавление к Function.prototype автоматически отразится на всех объектах, __proto__ которых ссылается на Function.prototype.

Замена Function.prototype конструктора другим объектом не обновит свойство __proto__ ни для одного из уже существующих объектов.

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

Чтобы ответить на первый вопрос, я создал специальную диаграмму ссылок __proto__ и prototype, к сожалению, stackoverflow не позволяет мне добавить изображение с репутацией «менее 10». Может в другой раз.

[Изменить] На рисунке используется [[Prototype]] вместо __proto__, потому что именно так спецификация ECMAScript относится к внутренним объектам. Надеюсь, ты сможешь во всем разобраться.

Вот несколько советов, которые помогут вам понять рисунок:

red    = JavaScript Function constructor and its prototype
violet = JavaScript Object constructor and its prototype
green  = user-created objects
         (first created using Object constructor or object literal {},
          second using user-defined constructor function)
blue   = user-defined function and its prototype
         (when you create a function, two objects are created in memory:
          the function and its prototype)

Обратите внимание, что свойство constructor не существует в созданных объектах, а наследуется от прототипа.

введите здесь описание изображения

person xorcus    schedule 06.02.2013
comment
@xorcus Не могли бы вы объяснить это: new MyFunction() создает экземпляр объекта, который его __proto__ должен ссылаться на его прототип ctor, который является MyFunction.prototype. Итак, почему MyFunction.prototype.__proto__ ссылается на Object.prototype? он должен ссылаться (как и мой первый образец) на прототип своего ctor, который является MyFunction.prototype (обратите внимание, что MyFunction.prototype является экземпляром Myfunction) - person Royi Namir; 02.11.2013
comment
@Royi Namir: MyFunction.prototype.__proto__ относится к Object.prototype, потому что MyFunction.prototype — это объект. Object.prototype наследуется всеми объектами (обычно на этом цепочка наследования прототипов заканчивается). Я не согласен с тем, что MyFunction.prototype является экземпляром MyFunction. obj instanceof MyFunction ‹=> MyFunction.prototype.isPrototypeOf(obj) ‹=> MyFunction.prototype существует в цепочке прототипов obj. Это не относится к объекту MyFunction.prototype. - person xorcus; 20.05.2015

Object — это Ева, а Function — это Адам, Адам (Function) использует свою кость (Function.prototype), чтобы создать Еву (Object). Тогда кто создал Адама (Function)? -- Изобретатель языка JavaScript :-).

Согласно ответу utsaina, я хочу добавить больше полезной информации.

Самым удивительным для меня было обнаружить, что Object.__proto__ указывает на Function.prototype, а не на Object.prototype, но я уверен, что на это есть веская причина :-)

Так не должно быть. Object.__proto__ НЕ ДОЛЖНО указывать на Object.prototype. Вместо этого экземпляр Object o, o.__proto__ должен указывать на Object.prototype.

(Простите меня за использование терминов class и instance в JavaScript, но вы это знаете :-)

Я думаю, что сам класс Object является экземпляром Function, поэтому Object.__proto__ === Function.prototype. Следовательно: Object — это Ева, а Function — это Адам, Адам (Function) использует свою кость (Function.prototype), чтобы создать Еву (Object).

Более того, даже сам класс Function является экземпляром самого Function, то есть Function.__proto__ === Function.prototype, поэтому и Function === Function.constructor

Кроме того, обычный класс Cat является экземпляром Function, то есть Cat.__proto__ === Function.prototype.

Причина вышеизложенного в том, что когда мы создаем класс в JavaScript, на самом деле мы просто создаем функцию, которая должна быть экземпляром Function. Object и Function просто специальные, но они все равно классы, а Cat обычный класс.

В частности, в движке Google Chrome JavaScript следующие 4:

  • Function.prototype
  • Function.__proto__
  • Object.__proto__
  • Cat.__proto__

Все они === (абсолютно равны) остальным 3, и их значение равно function Empty() {}

> Function.prototype
  function Empty() {}
> Function.__proto__
  function Empty() {}
> Object.__proto__
  function Empty() {}
> Cat.__proto__
  function Empty() {}
> Function.prototype === Function.__proto__
  true
> Function.__proto__ === Object.__proto__
  true
> Object.__proto__ === Cat.__proto__
  true

OK. Тогда кто создает специальные function Empty() {} (Function.prototype)? Подумай об этом :-)

person Peter Lee    schedule 27.07.2012
comment
Согласитесь с этим, за исключением последнего: что function Empty() {} вы называете равным Function.prototype и т. д.? Какой код вы использовали в консоли Chrome? - person drodsou; 28.07.2012
comment
Я исправил последнюю вещь, которую вы указали. Их значение равно function Empty() {} в Google Chrome. Я также добавил вывод консоли. - person Peter Lee; 30.07.2012
comment
все функции являются instanceof Function, и поэтому все функции наследуют (_ _proto_ _) от Function.prototype. Вот так просто :) - person xorcus; 20.05.2015
comment
Извините, что комментирую старую тему. Но созданы ли они Изобретателем языка? - person Patel Parth; 05.03.2019

Я действительно не знаю, почему люди не поправили вас в том, где реальная проблема в вашем понимании.

Это значительно облегчит вам обнаружение проблемы

Итак, давайте посмотрим, что происходит:

var newtoy = new Gadget("webcam", "black")

newtoy 
  .constructor //newtoy's constructor function is newtoy ( the function itself)
    .prototype // the function has a prototype property.( all functions has)
      .constructor // constructor here is a **property** (why ? becuase you just did `prototype.constructor`... see the dot ? )  ! it is not(!) the constructor function  !!! this is where your mess begins. it points back to the constructor function itself ( newtoy function)
         .prototype // so again we are at line 3 of this code snippet
            .constructor //same as line 4 ...
                .prototype 
                 rating = 3

Отлично, а теперь давайте посмотрим на это __proto__

Перед этим, пожалуйста, запомните 2 вещи относительно __proto__:

  1. Когда вы создаете объект с помощью оператора new, его внутреннее свойство [[Prototype]]/proto__ будет установлено на свойство prototype(1) его constructor function или "создателя", если хотите.

  2. Жестко запрограммировано в JS: Object.prototype.__proto__ равно null.

Обозначим эти 2 точки как "bill"

newtoy
     .__proto__ // When `newtoy` was created , Js put __proto__'s value equal to the value of the cunstructor's prototype value. which is `Gadget.prototype`.
       .__proto__ // Ok so now our starting point is `Gadget.prototype`. so  regarding "bill" who is the constructor function now? watch out !! it's a simple object ! a regular object ! prototype is a regular object!! so who is the constructor function of that object ? Right , it's the `function Object(){...}`.  Ok .( continuing "bill" ) does it has a `prototype` property ? sure. all function has. it's `Object.prototype`. just remember that when Gadget.prototype was created , it's internal `__proto__` was refered to `Object.prototype` becuase as "bill" says :"..will be set to the `prototype` property of   its `constructor function`"
          .__proto__ // Ok so now our satrting point is `Object.prototype`. STOP. read bullet 2.Object.prototype.__proto__ is null by definition. when Object.prototype ( as an object) was created , they SET THE __PROTO__ AS NULL HARDCODED

Лучше?

person Royi Namir    schedule 06.06.2015

Каждая функция создает свой прототип. И когда мы создадим объект с помощью конструктора этой функции, свойство __proto__ моего объекта начнет указывать на прототип этой функции.

person Apoorv Nag    schedule 29.12.2013
comment
Я думаю, вы имели в виду свойство __proto__. - person demisx; 25.08.2014
comment
да. Я имел в виду свойство proto объекта. Надеюсь информация была полезной. - person Apoorv Nag; 14.11.2014

Если все эти цифры были ошеломляющими, давайте посмотрим, что означают свойства.

STH.prototype

При создании новой функции параллельно создается пустой объект, который связан с функцией цепочкой [[Prototype]]. Для доступа к этому объекту мы используем свойство prototype функции.

function Gadget() {}
// in background, new object has been created
// we can access it with Gadget.prototype
// it looks somewhat like {constructor: Gadget}

Помните, что свойство prototype доступно только для функций.

СТХ.конструктор

У упомянутого выше объекта-прототипа нет свойств, кроме одного — constructor. Это свойство представляет функцию, которая создала объект-прототип.

var toy = new Gadget();

При создании функции Gadget мы также создали объект типа {constructor: Gadget} — это совсем не похоже на Gadget.prototype. Поскольку constructor относится к функции, создавшей прототип объекта, toy.constructor представляет функцию Gadget. Пишем toy.constructor.prototype и снова получаем {constructor: Gadget}.

Таким образом, возникает замкнутый круг: вы можете использовать toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype, и всегда будет Gadget.prototype.

toy
.constructor    // Gadget
.prototype    // {constructor: Gadget}
.constructor    // Gadget
.prototype    // {constructor: Gadget}
// ...

СТХ.__прото__

В то время как prototype является свойством, специфичным для функций, __proto__ доступно для всех объектов, поскольку оно находится в Object.prototype. Это относится к прототипу функции, которая может создать объект.

[].__proto__ === Array.prototype
// true

({}).__proto === Object.prototype
// true

Здесь toy.__proto__ равно Gadget.prototype. Поскольку Gadget.prototype — это объект ({}), а объекты создаются с помощью функции Object (см. пример выше), мы получаем Object.prototype. Это высший объект в JavaScript, и его __proto__ может указывать только на null.

toy
.__proto__    // Gadget.prototype (object looking like {constructor: Gadget})
.__proto__    // Object.prototype (topmost object in JS)
.__proto__    // null - Object.prototype is the end of any chain
person Damian Czapiewski    schedule 06.06.2018

Краткий ответ: __proto__ — это ссылка на свойство prototype конструктора, создавшего объект.

Объекты в JavaScript

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

Конструкторы в JavaScript

Функции — это обычные объекты (которые реализуют [[Call]] в терминах ECMA-262) с дополнительной возможностью вызова, но играют другую роль в JavaScript: они становятся конструкторами (фабриками для объектов), если вызываются с помощью оператора new. Таким образом, конструкторы являются грубым аналогом классов в других языках.

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

Конструктор prototype является своего рода планом для создания объектов, поскольку каждый объект, созданный конструктором, наследует ссылку на его prototype.

Цепочка прототипов

Объект указывает свой прототип через внутреннее свойство [[Prototype]] или __proto__. Отношение прототипа между двумя объектами основано на наследовании: каждый объект может иметь другой объект в качестве своего прототипа. Прототипом может быть значение null.

Цепочка объектов, связанных свойством __proto__, называется цепочкой прототипов. Когда делается ссылка на свойство в объекте, эта ссылка относится к свойству, встречающемуся в первом объекте в цепочке прототипов, который содержит свойство с таким именем. Цепочка прототипов ведет себя так, как если бы это был один объект.

Посмотрите это изображение (из этого блога):

прото.jpg

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

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

Почти все объекты являются экземплярами Object, потому что Object.prototype является последним в их цепочке прототипов. Но Object.prototype не является экземпляром Object, потому что Object.prototype.__proto__ содержит значение null.

Вы также можете создать объект с прототипом null следующим образом:

var dict = Object.create(null);

Такой объект является лучшей картой (словарем), чем литеральный объект, поэтому этот шаблон иногда называют шаблоном dict (dict для словаря).

Примечание: литеральные объекты, созданные с использованием {}, являются экземплярами Object, поскольку ({}).__proto__ является ссылкой на Object.prototype.

person explogx    schedule 29.05.2020
comment
@Bergi Берги я процитировал источник изображения. Большинство цитат, которые я использовал, взяты либо из стандарта JS, либо из MDN. - person explogx; 02.06.2020