Чем __proto__ отличается от constructor.prototype?

163
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__свойства?

xdevel2000
источник
30
Эта диаграмма график поможет понять разницу между прототипом и прото . Вы можете следить за прото цепочку от newtoy объекта, а затем вы поймете , почему третий Прото из newtoy является недействительным.
биты
Также из диаграммы ясно, что newtoy.prototypeона не равна newtoy.constructor.prototypeи, следовательно newtoy.constructor.prototype, не будет вызываться свойство rating. Точно так newtoy.constructor.prototype.constructor.propertyже не будет называться свойство rating.
биты
Опечатка в последнем комментарии: поэтому newtoy.constructor.prototypeбудет иметь свойство под названием рейтинг. Точно так же newtoy.constructor.prototype.constructor.propertyбудет иметь свойство под названием рейтинг.
биты
1
@Royi Namir Я загрузил jsViz на github. Вот демонстрационный сайт . Пожалуйста, не обращайте внимания на то, насколько незатейливым (и грязным) является сам код. Это супер старый проект, который я не затрагивал вечно.
биты

Ответы:

210

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

http://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);
drodsou
источник
2
@utsaina Очень круто. Оформить заказ в другом графическом представлении кода, который ОП разместил. И я думаю, что наши диаграммы согласуются с точки зрения технических деталей.
биты
43
Причина, по которой Object.__proto__указывается на это, Function.prototypeзаключается в том, что Object()сама по себе является нативной функцией, которая создает пустой объект. Следовательно, Object()это функция. Вы обнаружите, что __proto__свойства всех других основных нативных типов указывают на Function.prototype. Object, Function, String, Number, И Arrayвсе унаследуют функциональный прототип.
Поворот
@drodsou твоя вторая ссылка потрясающая. Пожалуйста, проверьте это сейчас;) mollypages.org/misc/js.mp Хорошее объяснение: D
abhisekp
@Swivel «Следовательно, Object () - это функция». Вы хотели сказать, что Object - это функция? без ()
Гиоргим
2
@GiorgiMoniava Правильно. Objectсам по себе является функцией; результат выполнения вызываемого Object(то есть возвращаемого значения выполнения Object()) не является функцией.
Поворот
67

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

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

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

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

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

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

Christoph
источник
3
Эта ссылка «здесь» является золотым стандартом. Зайдите туда, если хотите полное описание.
Ricalsin
Хороший улов с .constructor.prototypeцепочкой. Мне также было неясно для меня, хотя я не видел, что .constructorравно .__proto__.constructor. Это просто означает циклический переход между функцией конструктора и ее прототипом.
Johnny_D
30

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

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

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

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

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

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

[Edit] Рисунок использует [[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свойство не существует в созданных объектах, но наследуется от прототипа.

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

xorcus
источник
@xorcus Не могли бы вы объяснить это: new MyFunction()создает экземпляр объекта, который __proto__должен ссылаться на его прототип ctor. MyFunction.prototype.Так почему же MyFunction.prototype.__proto__ссылка на него Object.prototype? он должен ссылаться (как мой первый образец) на прототип своего ctor, который является MyFunction.prototype(обратите внимание, что MyFunction.prototypeэто пример Myfunction)
Royi Namir
@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
xorcus
14

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

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

Самым удивительным для меня было обнаружение того, на что 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

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

Питер Ли
источник
Согласитесь с этим, за исключением последнего: что function Empty() {}вы называете равным Function.prototype и т. Д., Какой код вы использовали в консоли chrome?
drodsou
2
Я исправил последнюю вещь, которую вы указали. Их значение function Empty() {}в Google Chrome. Я также добавил консольный вывод.
Питер Ли
все функции являются экземплярами Function, и, следовательно, все функции унаследованы ( _ _proto_ _) от Function.prototype. Это так просто :)
xorcus
Извините за комментирование старой темы. Но созданы ли они Изобретателем языка?
Патель Парт
6

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

Это облегчит вам задачу

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

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

Лучше?

Ройи Намир
источник
2

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

Апурв Наг
источник
1
Я думаю, что вы хотели сказать __proto__собственность.
demisx
Да. Я имел в виду прото свойство объекта. Надеюсь информация была полезной.
Апурв Наг
2

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

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свойство доступно только для функций.

STH.constructor

Упомянутый выше объект-прототип не имеет свойств, кроме одного - 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}
// ...

STH .__ proto__

Хотя 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
Дамиан Чапевский
источник
0

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

Объекты в JavaScript

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

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

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

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

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

Прототип цепи

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

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

Смотрите это изображение (извлечено из этого блога ):

proto.jpg

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

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

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

Вы также можете создать объект с nullпрототипом, например так:

var dict = Object.create(null);

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

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

eigenslacker
источник
Пожалуйста, укажите источник цитат и артефактов, которые вы используете. Похоже, что это изображение с giamir.com/pseudoclasses-and-prototypal-inheritance-in-JS , у вас есть авторские права на него?
Берги
@ Bergi Я процитировал источник изображения. Большинство цитат, которые я использовал, взяты из стандарта JS или MDN
eigenslacker