Nehmen Sie diese 2 Beispiele:
var A = function() { this.hey = function() { alert('from A') } };
vs.
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
Die meisten Leute hier (insbesondere die am besten bewerteten Antworten) versuchten zu erklären, wie sie sich unterscheiden, ohne zu erklären, WARUM. Ich denke, das ist falsch und wenn Sie zuerst die Grundlagen verstehen, wird der Unterschied offensichtlich. Versuchen wir zunächst, die Grundlagen zu erklären ...
a) Eine Funktion ist ein Objekt in JavaScript. JEDES Objekt in JavaScript erhält eine interne Eigenschaft (dh Sie können nicht wie andere Eigenschaften darauf zugreifen, außer vielleicht in Browsern wie Chrome), die häufig als bezeichnet wird __proto__(Sie können tatsächlich anyObject.__proto__Chrome eingeben, um zu sehen, worauf es verweist. Dies ist genau das , eine Eigenschaft, nichts weiter. Eine Eigenschaft in JavaScript = eine Variable innerhalb eines Objekts, nichts weiter. Was machen Variablen? Sie zeigen auf Dinge.
Worauf weist diese __proto__Eigenschaft hin? Nun, normalerweise ein anderes Objekt (wir werden später erklären, warum). Die einzige Möglichkeit, JavaScript zu erzwingen, damit die __proto__Eigenschaft NICHT auf ein anderes Objekt verweist, ist die Verwendung var newObj = Object.create(null). Selbst wenn Sie dies tun, existiert die __proto__Eigenschaft STILL als Eigenschaft des Objekts, nur zeigt sie nicht auf ein anderes Objekt, sondern aufnull .
Hier sind die meisten Leute verwirrt:
Wenn Sie eine neue Funktion in JavaScript erstellen (was auch ein Objekt ist, erinnern Sie sich?), Erstellt JavaScript in dem Moment, in dem es definiert wird, automatisch eine neue Eigenschaft für diese Funktion namens prototype. Versuch es:
var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined
A.prototypeist völlig anders als die __proto__Eigenschaft. In unserem Beispiel hat 'A' jetzt ZWEI Eigenschaften, die als 'Prototyp' und 'bezeichnet werden __proto__. Dies ist eine große Verwirrung für die Menschen. prototypeund __proto__Eigenschaften sind in keiner Weise miteinander verbunden, sie sind separate Dinge, die auf separate Werte verweisen.
Sie fragen sich vielleicht: Warum hat JavaScript __proto__für jedes einzelne Objekt eine Eigenschaft erstellt? Nun, ein Wort: Delegation . Wenn Sie eine Eigenschaft für ein Objekt aufrufen und das Objekt sie nicht hat, sucht JavaScript nach dem Objekt, auf das verwiesen wird, __proto__um festzustellen, ob es möglicherweise vorhanden ist. Wenn es nicht vorhanden ist, wird die __proto__Eigenschaft dieses Objekts usw. überprüft, bis die Kette endet. So der Name Prototypkette . Wenn JavaScript __proto__nicht auf ein Objekt zeigt und stattdessen auf ein nullPech zeigt, erkennt JavaScript dies und gibt Sie undefinedfür die Eigenschaft zurück.
Sie fragen sich vielleicht auch, warum JavaScript eine Eigenschaft erstellt, die prototypefür eine Funktion aufgerufen wird, wenn Sie die Funktion definieren. Weil es versucht, Sie zu täuschen, täuschen Sie, dass es wie klassenbasierte Sprachen funktioniert.
Fahren wir mit unserem Beispiel fort und erstellen ein "Objekt" aus A:
var a1 = new A();
Es passiert etwas im Hintergrund, als dieses Ding passiert ist. a1ist eine gewöhnliche Variable, der ein neues, leeres Objekt zugewiesen wurde.
Die Tatsache, dass Sie den Operator newvor einem Funktionsaufruf verwendet A()haben, hat im Hintergrund etwas ZUSÄTZLICHES bewirkt. Das newSchlüsselwort hat ein neues Objekt erstellt, auf das jetzt verwiesen wird, a1und dieses Objekt ist leer. Folgendes passiert zusätzlich:
Wir haben gesagt, dass für jede Funktionsdefinition eine neue Eigenschaft namens erstellt wird prototype(auf die Sie im Gegensatz zur __proto__Eigenschaft zugreifen können ). Nun, diese Eigenschaft wird jetzt verwendet.
Wir sind jetzt an dem Punkt angelangt, an dem wir ein frisch gebackenes leeres a1Objekt haben. Wir haben gesagt, dass alle Objekte in JavaScript eine interne __proto__Eigenschaft haben, die auf etwas verweist ( a1auch darauf), ob es null oder ein anderes Objekt ist. Der newOperator setzt diese __proto__Eigenschaft so, dass sie auf die Eigenschaft der Funktion verweist prototype. Lesen Sie das noch einmal. Es ist im Grunde das:
a1.__proto__ = A.prototype;
Wir sagten, das A.prototypesei nichts weiter als ein leeres Objekt (es sei denn, wir ändern es vor der Definition in etwas anderes a1). Zeigt nun also im Grunde a1.__proto__auf dasselbe A.prototype, worauf das leere Objekt verweist. Beide zeigen auf dasselbe Objekt, das beim Auftreten dieser Zeile erstellt wurde:
A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}
Jetzt passiert noch etwas, wenn die var a1 = new A()Anweisung verarbeitet wird. Grundsätzlich A()wird ausgeführt und wenn A so etwas ist:
var A = function() { this.hey = function() { alert('from A') } };
All das Zeug im Inneren function() { }wird ausgeführt. Wenn Sie die this.hey..Linie erreichen, thiswird in geändert a1und Sie erhalten Folgendes:
a1.hey = function() { alert('from A') }
Ich werde nicht erläutern, warum thisÄnderungen an vorgenommen wurden, a1aber dies ist eine großartige Antwort , um mehr zu erfahren.
Zusammenfassend lässt sich sagen, var a1 = new A()dass im Hintergrund drei Dinge passieren:
- Ein völlig neues leeres Objekt wird erstellt und zugewiesen
a1.a1 = {}
a1.__proto__Die Eigenschaft wird so zugewiesen, dass sie auf dasselbe zeigt wie A.prototypeauf (ein anderes leeres Objekt {}).
Die Funktion A()wird ausgeführt, indem thisauf das neue, leere Objekt gesetzt wird, das in Schritt 1 erstellt wurde (lesen Sie die Antwort, auf die ich oben verwiesen habe, warum thisÄnderungen an vorgenommen wurden a1).
Versuchen wir nun, ein anderes Objekt zu erstellen:
var a2 = new A();
Die Schritte 1, 2, 3 werden wiederholt. Merkst du etwas Das Schlüsselwort ist wiederholen. Schritt 1: a2wird ein neues leeres Objekt sein, Schritt 2: seine __proto__Eigenschaft zeigt auf dasselbe, was A.prototypeauf und vor allem zeigt, Schritt 3: Funktion A()wird WIEDER ausgeführt, was bedeutet, dass a2eine heyEigenschaft erhalten wird, die eine Funktion enthält. a1und a2haben zwei SEPARATE Eigenschaften benannt, heydie auf 2 SEPARATE Funktionen verweisen! Wir haben jetzt doppelte Funktionen in denselben zwei verschiedenen Objekten, die dasselbe tun, oops ... Sie können sich die Auswirkungen auf den Speicher vorstellen, wenn 1000 Objekte mit erstellt werden new A, nachdem alle Funktionsdeklarationen mehr Speicher benötigen als so etwas wie die Nummer 2. Also Wie verhindern wir das?
Erinnern Sie sich, warum die __proto__Eigenschaft auf jedem Objekt existiert? Wenn Sie also die yoManEigenschaft abrufen a1(die nicht vorhanden ist), wird ihre __proto__Eigenschaft konsultiert. Wenn es sich um ein Objekt handelt (und dies in den meisten Fällen auch ist), wird überprüft, ob es enthält yoManund ob dies nicht der Fall ist. Es wird das Objekt __proto__usw. konsultieren . Wenn dies der Fall ist, wird dieser Eigenschaftswert verwendet und angezeigt.
Also hat sich jemand entschieden, diese Tatsache + die Tatsache zu verwenden, dass beim Erstellen a1die __proto__Eigenschaft auf dasselbe (leere) Objekt A.prototypeverweist und auf Folgendes verweist:
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
Cool! Wenn Sie jetzt erstellen a1, werden alle drei oben genannten Schritte erneut durchlaufen, und in Schritt 3 wird nichts ausgeführt, da function A()nichts ausgeführt werden muss. Und wenn wir das tun:
a1.hey
Es wird sehen, dass a1es nicht enthält, heyund es wird sein __proto__Eigenschaftsobjekt überprüfen, um festzustellen, ob es es hat, was der Fall ist.
Mit diesem Ansatz entfernen wir den Teil aus Schritt 3, in dem Funktionen bei jeder neuen Objekterstellung dupliziert werden. Anstelle von a1und a2mit einer separaten heyEigenschaft hat jetzt KEINE von ihnen diese. Was Sie wohl inzwischen selbst herausgefunden haben. Das ist das Schöne ... wenn Sie verstehen __proto__und Function.prototype, werden Fragen wie diese ziemlich offensichtlich sein.
HINWEIS: Einige Leute neigen dazu, die interne Prototype-Eigenschaft nicht als zu bezeichnen __proto__. Ich habe diesen Namen durch den Beitrag verwendet, um ihn klar von der Functional.prototypeEigenschaft als zwei verschiedene Dinge zu unterscheiden.