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.prototype
ist 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. prototype
und __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 null
Pech zeigt, erkennt JavaScript dies und gibt Sie undefined
für die Eigenschaft zurück.
Sie fragen sich vielleicht auch, warum JavaScript eine Eigenschaft erstellt, die prototype
fü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. a1
ist eine gewöhnliche Variable, der ein neues, leeres Objekt zugewiesen wurde.
Die Tatsache, dass Sie den Operator new
vor einem Funktionsaufruf verwendet A()
haben, hat im Hintergrund etwas ZUSÄTZLICHES bewirkt. Das new
Schlüsselwort hat ein neues Objekt erstellt, auf das jetzt verwiesen wird, a1
und 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 a1
Objekt haben. Wir haben gesagt, dass alle Objekte in JavaScript eine interne __proto__
Eigenschaft haben, die auf etwas verweist ( a1
auch darauf), ob es null oder ein anderes Objekt ist. Der new
Operator 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.prototype
sei 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, this
wird in geändert a1
und Sie erhalten Folgendes:
a1.hey = function() { alert('from A') }
Ich werde nicht erläutern, warum this
Änderungen an vorgenommen wurden, a1
aber 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.prototype
auf (ein anderes leeres Objekt {}).
Die Funktion A()
wird ausgeführt, indem this
auf 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: a2
wird ein neues leeres Objekt sein, Schritt 2: seine __proto__
Eigenschaft zeigt auf dasselbe, was A.prototype
auf und vor allem zeigt, Schritt 3: Funktion A()
wird WIEDER ausgeführt, was bedeutet, dass a2
eine hey
Eigenschaft erhalten wird, die eine Funktion enthält. a1
und a2
haben zwei SEPARATE Eigenschaften benannt, hey
die 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 yoMan
Eigenschaft 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 yoMan
und 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 a1
die __proto__
Eigenschaft auf dasselbe (leere) Objekt A.prototype
verweist 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 a1
es nicht enthält, hey
und 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 a1
und a2
mit einer separaten hey
Eigenschaft 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.prototype
Eigenschaft als zwei verschiedene Dinge zu unterscheiden.