Wie unterscheidet sich __proto__ vom Konstruktor.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 

Es gibt immer das Objekt mit der Bewertung = 3 zurück.

Aber wenn ich folgendes mache:

newtoy.__proto__.__proto__.__proto__

Die Kette kehrt schließlich zurück null.

Wie würde ich auch im Internet Explorer die Null überprüfen, wenn keine __proto__Eigenschaft vorhanden ist?

xdevel2000
quelle
30
Dieses Diagramm hilft Ihnen, den Unterschied zwischen Prototyp und Proto zu verstehen . Sie können die folgen Proto Kette von Newtoy Objekt, und dann werden Sie erkennen , warum die dritte Proto von Newtoy null ist.
Bits
Aus dem Diagramm geht auch hervor, dass die Eigenschaft newtoy.prototypenicht gleich ist newtoy.constructor.prototypeund daher newtoy.constructor.prototypenicht aufgerufen wird rating. Ebenso newtoy.constructor.prototype.constructor.propertywird auch keine Eigenschaft aufgerufen rating.
Bits
Tippfehler im letzten Kommentar: newtoy.constructor.prototypeWird daher eine Eigenschaft namens Rating haben. In ähnlicher Weise newtoy.constructor.prototype.constructor.propertywird auch eine Eigenschaft namens Rating haben.
Bits
1
@ Royi Namir Ich habe jsViz auf github hochgeladen . Hier ist die Demo-Site . Bitte haben Sie nichts dagegen, wie unbeaufsichtigt (und schmutzig) der eigentliche Code ist. Es ist ein super altes Projekt, das ich nicht für immer berührt habe.
Bits

Antworten:

210

Ich habe kürzlich versucht, meinen Kopf darum zu wickeln, und schließlich diese "Karte" entwickelt, von der ich denke, dass sie das ganze Licht über die Sache wirft

http://i.stack.imgur.com/KFzI3.png Geben Sie hier die Bildbeschreibung ein

Ich weiß, dass ich nicht der erste bin, der das erfunden hat, aber es war interessanter herauszufinden, als es zu finden :-). Wie auch immer, danach habe ich zB dieses andere Diagramm gefunden, von dem ich denke, dass es im Grunde dasselbe sagt:

Javascript-Objektlayout

Die überraschendste Sache für mich war die Entdeckung , dass die Object.__proto__Punkte Function.prototype, statt Object.prototype, aber ich bin sicher , es gibt einen guten Grund dafür :-)

Ich füge den im Bild erwähnten Code auch hier ein, wenn jemand ihn testen möchte. Beachten Sie, dass den Objekten einige Eigenschaften hinzugefügt werden, damit Sie nach einigen Sprüngen leicht erkennen können, wo wir uns befinden:

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
quelle
2
@utsaina Sehr cool. Überprüfen Sie eine weitere grafische Darstellung des Codes, den OP veröffentlicht hat. Und ich denke, unsere Diagramme stimmen in Bezug auf technische Details überein.
Bits
43
Der Grund, warum darauf Object.__proto__hingewiesen wird, Function.prototypeist, dass es Object()sich um eine native Funktion handelt, die ein leeres Objekt instanziiert. Daher Object()ist eine Funktion. Sie werden feststellen, dass alle __proto__Eigenschaften der anderen wichtigen nativen Typen darauf verweisen Function.prototype. Object, Function, String, Number, Und Arrayalle erben den Funktionsprototyp.
Swivel
@drodsou dein 2. Link ist super. Probieren Sie es jetzt bitte aus;) mollypages.org/misc/js.mp Schöne Erklärung: D
abhisekp
@Swivel "Daher ist Object () eine Funktion" - wollten Sie sagen, dass Object eine Funktion ist? ohne ()
Giorgim
2
@GiorgiMoniava Richtig. Objectselbst ist eine Funktion; Das Ergebnis der Ausführung von callable Object(dh der Rückgabewert von running Object()) ist keine Funktion.
Wirbel
67

constructorist eine vordefinierte [[DontEnum]] - Eigenschaft des Objekts, auf die die prototypeEigenschaft eines Funktionsobjekts zeigt, und zeigt zunächst auf das Funktionsobjekt selbst.

__proto__ entspricht der internen Eigenschaft eines Objekts, dh seines tatsächlichen Prototyps.

Wenn Sie ein Objekt mit dem newOperator erstellen , wird seine interne Eigenschaft [[Prototype]] auf das Objekt festgelegt, auf das die prototypeEigenschaft der Konstruktorfunktion zeigt .

Dies bedeutet, dass die zum Erstellen des Objekts verwendete Konstruktorfunktion .constructorausgewertet wird .__proto__.constructor, und wie wir erfahren haben, wurde die protoypeEigenschaft dieser Funktion zum Festlegen des [[Prototyps]] des Objekts verwendet.

Daraus folgt, dass dies .constructor.prototype.constructoridentisch ist mit .constructor(solange diese Eigenschaften nicht überschrieben wurden); siehe hier für eine ausführlichere Erklärung.

Wenn __proto__verfügbar, können Sie die eigentliche Prototypkette des Objekts durchlaufen. In einfachem ECMAScript3 ist dies nicht möglich, da JavaScript nicht für Hierarchien mit tiefer Vererbung entwickelt wurde.

Christoph
quelle
3
Dieser "hier" -Link ist der Goldstandard. Gehen Sie dorthin, wenn Sie die vollständige Beschreibung wünschen.
Ricalsin
Schöner Fang mit .constructor.prototypeVerkettung. Ich war mir auch unklar, obwohl ich nicht gesehen habe, dass .constructordas gleich ist .__proto__.constructor. Was einfach bedeutet, zwischen der Konstruktorfunktion und ihrem Prototyp zu wechseln.
Johnny_D
30

Die prototypische Vererbung in JavaScript basiert auf einer __proto__Eigenschaft in dem Sinne, dass jedes Objekt den Inhalt des Objekts erbt, auf das seine __proto__Eigenschaft verweist .

Die prototypeEigenschaft ist nur für FunctionObjekte und nur dann newspezifisch, wenn der Operator zum Aufrufen eines Functionals Konstruktor verwendet wird. In diesem Fall werden die erstellten Objekte __proto__auf Konstruktoren gesetzt Function.prototype.

Dies bedeutet, dass das Hinzufügen zu Function.prototypeautomatisch alle Objekte widerspiegelt, auf die __proto__verwiesen wird Function.prototype.

Ersetzen Konstruktor Function.prototypemit einem anderen Objekt nicht aktualisieren __proto__Eigenschaft für eine der bereits vorhandenen Objekte.

Beachten Sie, dass auf die __proto__Eigenschaft nicht direkt zugegriffen werden sollte. Stattdessen sollte Object.getPrototypeOf (Objekt) verwendet werden.

Um die erste Frage zu beantworten, habe ich ein maßgeschneidertes Diagramm __proto__und prototypeReferenzen erstellt. Leider erlaubt mir der Stackoverflow nicht, das Bild mit "weniger als 10 Reputation" hinzuzufügen. Vielleicht ein anderes Mal.

[Bearbeiten] In der Abbildung wird [[Prototype]]anstelle von verwendet, __proto__da sich die ECMAScript-Spezifikation auf interne Objekte bezieht. Ich hoffe du kannst alles herausfinden.

Hier sind einige Hinweise, die Ihnen helfen, die Abbildung zu verstehen:

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)

Beachten Sie, dass die constructorEigenschaft in erstellten Objekten nicht vorhanden ist, sondern vom Prototyp geerbt wird.

Geben Sie hier die Bildbeschreibung ein

Xorcus
quelle
@xorcus Können Sie bitte das erklären: new MyFunction()erstellt eine Objektinstanz , die sein __proto__auf seinen Ctor Prototyp beziehen sollte , die ist MyFunction.prototype.also warum tut MyFunction.prototype.__proto__referes zu Object.prototype? es sollte sich (wie mein erstes Beispiel) auf den Prototyp seines Ctors beziehen, der MyFunction.prototype(beachten Sie, dass dies MyFunction.prototypeein Beispiel ist Myfunction)
Royi Namir
@ Royi Namir: MyFunction.prototype .__ proto__ bezieht sich auf Object.prototype, da MyFunction.prototype ein Objekt ist. Object.prototype wird von allen Objekten geerbt (normalerweise endet hier die Vererbungskette des Prototyps). Ich bin nicht damit einverstanden, dass MyFunction.prototype eine Instanz von MyFunction ist. obj-Instanz von MyFunction <=> MyFunction.prototype.isPrototypeOf (obj) <=> MyFunction.prototype ist in der obj-Prototypenkette vorhanden. Dies ist nicht der Fall für MyFunction.prototype Objekt
Xorcus
14

Objectist Eva und Functionist Adam, Adam ( Function) benutzt seinen Knochen ( Function.prototype), um Eva ( Object) zu erschaffen . Wer hat dann Adam ( Function) erschaffen ? - Der Erfinder der JavaScript-Sprache :-).

Nach der Antwort von utsaina möchte ich weitere nützliche Informationen hinzufügen.

Die überraschendste Sache für mich war die Entdeckung , dass die Object.__proto__ Punkte Function.prototype, statt Object.prototype, aber ich bin sicher , es gibt einen guten Grund dafür :-)

Es sollte nicht sein. Object.__proto__sollte NICHT auf zeigen Object.prototype. Stattdessen wird die Instanz Object o, o.__proto__sollte darauf zu Object.prototype.

(Verzeihen Sie mir, dass ich die Begriffe classund instanceJavaScript verwende, aber Sie wissen es :-)

Ich denke, die Klasse Objectselbst ist ein Beispiel dafür Function, deshalb Object.__proto__ === Function.prototype. Deshalb: Objectist Eva und Functionist Adam, Adam ( Function) benutzt seinen Knochen ( Function.prototype), um Eva ( Object) zu erschaffen .

Darüber hinaus ist sogar die Klasse Functionselbst eine Instanz von sich Functionselbst, das ist Function.__proto__ === Function.prototypeauch der GrundFunction === Function.constructor

Darüber hinaus ist die reguläre Klasse Cateine Instanz von Function, das heißt Cat.__proto__ === Function.prototype.

Der Grund dafür ist, dass wir beim Erstellen einer Klasse in JavaScript tatsächlich nur eine Funktion erstellen, für die eine Instanz sein sollte Function. Objectund Functionsind nur etwas Besonderes, aber sie sind immer noch Klassen, während Cates sich um eine reguläre Klasse handelt.

In der Google Chrome JavaScript-Engine werden die folgenden 4 Faktoren berücksichtigt:

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

Sie sind alle ===(absolut gleich) zu den anderen 3 und ihr Wert istfunction 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. Wer erstellt dann das Special function Empty() {}( Function.prototype)? Denk darüber nach :-)

Peter Lee
quelle
Stimmen Sie dem zu, mit Ausnahme der letzten Sache: Was bedeutet es function Empty() {}, Function.prototype usw. zu entsprechen? Welchen Code haben Sie in der Chrome-Konsole verwendet?
Drodsou
2
Ich habe das letzte korrigiert, worauf Sie hingewiesen haben. Ihr Wert ist function Empty() {}in Google Chrome. Ich habe auch die Konsolenausgabe hinzugefügt.
Peter Lee
Alle Funktionen sind Instanzen von Function, und daher erben alle Funktionen ( _ _proto_ _) von Function.prototype. So einfach ist das :)
Xorcus
Entschuldigung für den Kommentar zum alten Thread. Aber werden sie vom Erfinder der Sprache erstellt?
Patel Parth
6

Ich weiß wirklich nicht, warum die Leute dich nicht korrigiert haben, wo das eigentliche Problem in deinem Verständnis liegt.

Dies würde es Ihnen viel leichter machen, das Problem zu erkennen

Mal sehen, was los ist:

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

Großartig, jetzt schauen wir uns das an __proto__

Bitte denken Sie vorher an zwei Dinge __proto__ :

  1. Wenn Sie ein Objekt mit dem newOperator erstellen , wird dessen interne [[Prototype]]/ proto__Eigenschaft auf die prototypeEigenschaft (1) seines constructor functionoder "Erstellers" gesetzt, wenn Sie möchten.

  2. In JS fest codiert -: Object.prototype.__proto__ist null.

Nennen wir diese beiden Punkte " 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

Besser?

Royi Namir
quelle
2

Jede Funktion erstellt ihren Prototyp. Wenn wir mit diesem Funktionskonstruktor ein Objekt erstellen, zeigt die Eigenschaft __proto__ meines Objekts auf den Prototyp dieser Funktion.

Apoorv Nag
quelle
1
Ich denke, Sie wollten das __proto__Eigentum sagen .
Demisx
Ja. Ich meinte Proto- Eigenschaft eines Objekts. Ich hoffe, die Informationen waren nützlich.
Apoorv Nag
2

Wenn all diese Zahlen überwältigend waren, schauen wir uns an, was die Eigenschaften bedeuten.

STH.prototype

Beim Erstellen einer neuen Funktion wird parallel ein leeres Objekt erstellt und mit der Funktion über die [[Prototype]]Kette verknüpft . Um auf dieses Objekt zuzugreifen, verwenden wir die prototypeEigenschaft der Funktion.

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

Beachten Sie, dass die prototypeEigenschaft nur für Funktionen verfügbar ist.

STH.constructor

Das oben erwähnte Prototypobjekt hat keine Eigenschaften außer einem - constructor. Diese Eigenschaft stellt eine Funktion dar, mit der das Prototypobjekt erstellt wurde.

var toy = new Gadget();

Beim Erstellen einer GadgetFunktion haben wir auch ein Objekt wie erstellt {constructor: Gadget}- das ist nichts dergleichen Gadget.prototype. Wie constructorauf eine Funktion bezieht , die ein Objekt - Prototyp erstellt, toy.constructordarstellt GadgetFunktion. Wir schreiben toy.constructor.prototypeund wir bekommen {constructor: Gadget}wieder.

Daher gibt es einen Teufelskreis: Sie können verwenden toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototypeund es wird immer sein Gadget.prototype.

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

STH .__ proto__

Während prototypees sich um eine funktionsspezifische Eigenschaft handelt, __proto__ist sie für alle Objekte verfügbar, während sie sich einfügt Object.prototype. Es bezieht sich auf den Prototyp einer Funktion, die ein Objekt erstellen kann.

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

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

Hier toy.__proto__ist Gadget.prototype. Da Gadget.prototypeein Objekt ( {}) und Objekte mit ObjectFunktion erstellt werden (siehe obiges Beispiel), erhalten wir Object.prototype. Dies ist das höhere Objekt in JavaScript und __proto__kann nur anzeigen 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
Damian Czapiewski
quelle
0

Kurze Antwort: __proto__ist ein Verweis auf die prototypeEigenschaft des Konstruktors, der das Objekt erstellt hat.

Objekte in JavaScript

Ein JavaScript-Objekt ist ein integrierter Typ für eine Sammlung von null oder mehr Eigenschaften. Eigenschaften sind Container, die andere Objekte, Grundwerte oder Funktionen enthalten.

Konstruktoren in JavaScript

Funktionen sind reguläre Objekte (die [[Call]]in ECMA-262-Begriffen implementiert sind) mit der zusätzlichen Fähigkeit, aufrufbar zu sein, spielen jedoch eine andere Rolle in JavaScript: Sie werden zu Konstruktoren ( Fabriken für Objekte), wenn sie über den newOperator aufgerufen werden . Konstruktoren sind daher ein grobes Analogon zu Klassen in anderen Sprachen.

Jede JavaScript-Funktion ist tatsächlich eine Instanz des Functionintegrierten Funktionsobjekts mit einer speziellen Eigenschaft namens, prototypedie zum Implementieren der prototypbasierten Vererbung und der gemeinsamen Eigenschaften verwendet wird. Jedes von einer Konstruktorfunktion erstellte Objekt hat einen impliziten Verweis (als Prototyp oder bezeichnet __proto__) auf den Wert seines Konstruktors prototype.

Der Konstruktor prototypeist eine Art Blaupause zum Erstellen von Objekten, da jedes vom Konstruktor erstellte Objekt einen Verweis auf sein Objekt erbt prototype.

Die Prototypkette

Ein Objekt gibt seinen Prototyp über die interne Eigenschaft [[Prototype]]oder an __proto__. Bei der Prototypbeziehung zwischen zwei Objekten geht es um Vererbung: Jedes Objekt kann ein anderes Objekt als Prototyp haben. Der Prototyp kann der nullWert sein.

Die durch die __proto__Eigenschaft verbundene Objektkette wird als Prototypkette bezeichnet . Wenn auf eine Eigenschaft in einem Objekt verwiesen wird, bezieht sich diese Referenz auf die Eigenschaft, die im ersten Objekt in der Prototypenkette gefunden wurde, die eine Eigenschaft dieses Namens enthält. Die Prototypenkette verhält sich wie ein einzelnes Objekt.

Siehe dieses Bild (aus diesem Blog extrahiert ):

proto.jpg

Wenn Sie versuchen, auf eine Eigenschaft in einem Objekt zuzugreifen, startet JavaScript die Suche in diesem Objekt und setzt den Prototyp, den Prototyp des Prototyps usw. fort, bis die Eigenschaft gefunden wird oder wenn __proto__ der Wert vorhanden ist null.

Diese Art der Vererbung unter Verwendung der Prototypenkette wird häufig genannt Delegierung bezeichnet , um Verwechslungen mit anderen Sprachen unter Verwendung der Klassenkette zu vermeiden.

Fast alle Objekte sind Instanzen von Object, weil Object.prototypees das letzte in ihrer Prototypenkette ist. Ist Object.prototypeaber keine Instanz vonObject weil Object.prototype.__proto__hält den Wert null.

Sie können auch ein Objekt mit einem erstellen null Prototyp wie dem folgenden :

var dict = Object.create(null);

Ein solches Objekt ist eine bessere Karte (Wörterbuch) als ein wörtliches Objekt, weshalb dieses Muster manchmal als Diktat bezeichnet wird ( Diktat für Wörterbuch) bezeichnet wird.

Hinweis: wörtliche Objekte erstellt {}sind Instanzen , Objectda ({}).__proto__ist ein Verweis auf Object.prototype.

Eigenslacker
quelle
Bitte geben Sie die Quelle der von Ihnen verwendeten Zitate und Artefakte an. Das Bild scheint von giamir.com/pseudoclasses-and-prototypal-inheritance-in-JS zu stammen. Haben Sie das Urheberrecht daran?
Bergi
@Bergi Ich habe die Quelle des Bildes zitiert. Die meisten der von mir verwendeten Anführungszeichen stammen entweder aus dem JS-Standard oder dem MDN
Eigenslacker