Was ist der Unterschied zwischen
var A = function () {
this.x = function () {
//do something
};
};
et
var A = function () { };
A.prototype.x = function () {
//do something
};
Was ist der Unterschied zwischen
var A = function () {
this.x = function () {
//do something
};
};
et
var A = function () { };
A.prototype.x = function () {
//do something
};
Die Beispiele haben sehr unterschiedliche Ergebnisse.
Bevor Sie sich die Unterschiede ansehen, sollten Sie Folgendes beachten:
[[Prototype]]
Eigentum.myObj.method()
) dann diese innerhalb der Methode auf das Objekt verweist. Wo diese nicht durch den Aufruf oder durch die Verwendung von binden wird standardmäßig das globale Objekt (Fenster in einem Browser) verwendet, oder es bleibt im strengen Modus undefiniert.Hier sind also die fraglichen Ausschnitte:
var A = function () {
this.x = function () {
//do something
};
};
In diesem Fall ist die Variable A
wird ein Wert zugewiesen, der eine Referenz auf eine Funktion ist. Wenn diese Funktion aufgerufen wird mit A()
ist die Funktion diese wird durch den Aufruf nicht gesetzt, so dass es standardmäßig das globale Objekt und den Ausdruck this.x
wirksam ist window.x
. Das Ergebnis ist, dass ein Verweis auf den Funktionsausdruck auf der rechten Seite zugewiesen wird window.x
.
Im Fall von:
var A = function () { };
A.prototype.x = function () {
//do something
};
geschieht etwas ganz anderes. In der ersten Zeile ist die Variable A
eine Referenz auf eine Funktion zugewiesen wird. In JavaScript haben alle Funktionsobjekte eine Prototyp Eigenschaft standardmäßig, so dass es keinen separaten Code zur Erstellung einer A.Prototyp Objekt.
In der zweiten Zeile, A.prototyp.x eine Referenz auf eine Funktion zugewiesen wird. Dadurch wird eine x Eigenschaft, wenn sie nicht vorhanden ist, oder weisen Sie einen neuen Wert zu, wenn sie vorhanden ist. Der Unterschied zum ersten Beispiel, in dem die Eigenschaft des Objekts x Eigenschaft an dem Ausdruck beteiligt ist.
Ein weiteres Beispiel finden Sie unten. Es ist dem ersten ähnlich (und vielleicht das, wonach Sie fragen wollten):
var A = new function () {
this.x = function () {
//do something
};
};
In diesem Beispiel ist die new
Operator vor dem Funktionsausdruck eingefügt, damit die Funktion als Konstruktor aufgerufen wird. Beim Aufruf mit new
ist die Funktion diese auf ein neues Objekt verweist, dessen private [[Prototype]]
ist so eingestellt, dass sie auf die öffentliche Eigenschaft des Konstruktors Prototyp . In der Zuweisungsanweisung wird also die x
Eigenschaft wird für dieses neue Objekt erstellt. Wenn eine Funktion als Konstruktor aufgerufen wird, gibt sie ihre diese Objekt standardmäßig, so dass kein separates return this;
Erklärung.
Um zu überprüfen, dass A hat eine x Eigentum:
console.log(A.x) // function () {
// //do something
// };
Dies ist eine unübliche Verwendung von neu da die einzige Möglichkeit, den Konstruktor zu referenzieren, über A.Konstrukteur . Es wäre viel häufiger zu tun:
var A = function () {
this.x = function () {
//do something
};
};
var a = new A();
Eine andere Möglichkeit, ein ähnliches Ergebnis zu erzielen, ist die Verwendung eines unmittelbar aufgerufenen Funktionsausdrucks:
var A = (function () {
this.x = function () {
//do something
};
}());
In diesem Fall, A
den Rückgabewert des Aufrufs der Funktion auf der rechten Seite zugewiesen. Auch hier, da diese nicht gesetzt ist, verweist er auf das globale Objekt und this.x
wirksam ist window.x
. Da die Funktion nichts zurückgibt, A
hat einen Wert von undefined
.
Diese Unterschiede zwischen den beiden Ansätzen zeigen sich auch, wenn Sie Ihre Javascript-Objekte in/aus JSON serialisieren und de-serialisieren. Methoden, die im Prototyp eines Objekts definiert sind, werden nicht serialisiert, wenn Sie das Objekt serialisieren. Das kann praktisch sein, wenn Sie zum Beispiel nur die Daten eines Objekts serialisieren wollen, nicht aber seine Methoden:
var A = function () {
this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance));
// {"objectsOwnProperties":"are serialized"}
Verwandte Fragen :
Sidenote: Es kann sein, dass es zwischen den beiden Ansätzen keine signifikanten Speichereinsparungen gibt, aber die Verwendung des Prototyps zur gemeinsamen Nutzung von Methoden und Eigenschaften wird wahrscheinlich weniger Speicher verbrauchen als jede Instanz mit ihrer eigenen Kopie.
JavaScript ist keine einfache Sprache. Es ist vielleicht nicht sehr sinnvoll, Prototyping oder andere Vererbungsmuster als eine Möglichkeit zu betrachten, die Art und Weise der Speicherzuweisung explizit zu ändern.
Ist es nicht eigentlich so, dass jeder 機能 hat einen Prototyp ( no jede 物 )? Zum Beispiel wird Folgendes zurückgegeben undefined
: ({}).prototype
.
Ich verstehe Ihr Beispiel, aber es geht eigentlich um jedes Objekt. Du hast dort nur ein Objektliteral, und der Prototyp dieser einzelnen Instanz ist undefiniert. Werten Sie stattdessen Object.prototype aus. Ich nehme an, Sie haben dasselbe mit einer Funktionsinstanz versucht. Die Funktionsinstanz wird Ihnen einen guten Prototypwert liefern, da sie von Object erbt.
Wie bereits von anderen erwähnt, führt die Verwendung von "this" in der ersten Version dazu, dass jede Instanz der Klasse A ihre eigene unabhängige Kopie der Funktionsmethode "x" hat. Die Verwendung von "prototype" hingegen bedeutet, dass jede Instanz der Klasse A die gleiche Kopie der Methode "x" verwendet.
Der folgende Code soll diesen feinen Unterschied verdeutlichen:
// x is a method assigned to the object using "this"
var A = function () {
this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
this.x = function() { alert( value ); }
};
var a1 = new A();
var a2 = new A();
a1.x(); // Displays 'A'
a2.x(); // Also displays 'A'
a1.updateX('Z');
a1.x(); // Displays 'Z'
a2.x(); // Still displays 'A'
// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };
B.prototype.updateX = function( value ) {
B.prototype.x = function() { alert( value ); }
}
var b1 = new B();
var b2 = new B();
b1.x(); // Displays 'B'
b2.x(); // Also displays 'B'
b1.updateX('Y');
b1.x(); // Displays 'Y'
b2.x(); // Also displays 'Y' because by using prototype we have changed it for all instances
Wie andere bereits erwähnt haben, gibt es verschiedene Gründe, sich für die eine oder andere Methode zu entscheiden. Mein Beispiel soll nur den Unterschied deutlich machen.
Das ist das, was ich erwarten würde, aber wenn ich ein neues Objekt instanziiere, nachdem ich A.x wie oben geändert habe, wird immer noch "A" angezeigt, es sei denn, ich verwende A wie ein Singleton. jsbin.com/omida4/2/edit
Das liegt daran, dass mein Beispiel falsch war. Es ist erst seit zwei Jahren falsch. Seufz. Aber der Punkt ist immer noch gültig. Ich habe das Beispiel mit einem Beispiel aktualisiert, das tatsächlich funktioniert. Danke für den Hinweis.
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) haben versucht zu erklären, wie sie sich unterscheiden, ohne zu erklären, WARUM. Ich denke, das ist falsch, und wenn man zuerst die Grundlagen versteht, wird der Unterschied offensichtlich. Versuchen wir also zunächst, die Grundlagen zu erklären...
a) Eine Funktion ist ein Objekt in JavaScript. JEDES Objekt in JavaScript hat eine interne Eigenschaft (d.h. man kann nicht darauf zugreifen wie auf andere Eigenschaften, außer vielleicht in Browsern wie Chrome), oft bezeichnet als __proto__
(Sie können tatsächlich eingeben anyObject.__proto__
in Chrome, um zu sehen, worauf sie verweist. Dies ist genau das, eine Eigenschaft, nichts weiter. Eine Eigenschaft in JavaScript ist eine Variable innerhalb eines Objekts, mehr nicht. Was tun Variablen? Sie verweisen auf Dinge.
Was bedeutet dies also __proto__
Eigenschaft hinweist? Nun, normalerweise auf ein anderes Objekt (wir werden später erklären, warum). Die einzige Möglichkeit, JavaScript für die Eigenschaft __proto__
Eigenschaft NICHT auf ein anderes Objekt verweist, ist die Verwendung von var newObj = Object.create(null)
. Selbst wenn Sie dies tun, ist die __proto__
Eigenschaft STILL existiert als Eigenschaft des Objekts, nur zeigt sie nicht auf ein anderes Objekt, sondern auf null
.
Hier sind die meisten Menschen verwirrt:
Wenn Sie eine neue Funktion in JavaScript erstellen (die auch ein Objekt ist, erinnern Sie sich?), erstellt JavaScript bei ihrer Definition automatisch eine neue Eigenschaft für diese Funktion namens prototype
. Probieren Sie es aus:
var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined
A.prototype
ist VÖLLIG UNTERSCHIEDLICH von der __proto__
Eigenschaft. In unserem Beispiel hat "A" nun ZWEI Eigenschaften namens "Prototyp" und __proto__
. Das ist eine große Verwirrung für die Menschen. prototype
y __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__
Eigenschaft für jedes einzelne Objekt 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 durch __proto__
um zu sehen, ob es vielleicht vorhanden ist. Ist dies nicht der Fall, wird das Objekt in der __proto__
Eigenschaft und so weiter... bis die Kette endet. Daher der Name Prototypenkette . Natürlich, wenn __proto__
verweist nicht auf ein Objekt und zeigt stattdessen auf null
Pech gehabt, JavaScript hat das erkannt und wird Sie zurückschicken. undefined
für die Immobilie.
Sie fragen sich vielleicht auch, warum JavaScript eine Eigenschaft namens prototype
für eine Funktion, wenn Sie die Funktion definieren? Weil es versucht, Sie zu täuschen, ja Sie täuschen dass sie wie klassenbasierte Sprachen funktioniert.
Lassen Sie uns mit unserem Beispiel fortfahren und ein "Objekt" aus A
:
var a1 = new A();
Irgendetwas geschah im Hintergrund, als diese Sache passierte. a1
ist eine gewöhnliche Variable, der ein neues, leeres Objekt zugewiesen wurde.
Die Tatsache, dass Sie den Operator new
vor einem Funktionsaufruf A()
hat im Hintergrund etwas ZUSÄTZLICHES gemacht. Die new
Schlüsselwort ein neues Objekt erstellt, das nun auf a1
und dieses Objekt ist leer. Hier ist, was zusätzlich passiert:
Wir haben gesagt, dass bei jeder Funktionsdefinition eine neue Eigenschaft namens prototype
(auf die Sie, anders als auf die __proto__
Eigenschaft) erstellt? Nun, diese Eigenschaft wird jetzt verwendet.
Wir sind also an dem Punkt angelangt, an dem wir eine frisch gebackene leere a1
Objekt. Wir haben gesagt, dass alle Objekte in JavaScript eine interne __proto__
Eigenschaft, die auf etwas verweist ( a1
hat es auch), egal ob es null oder ein anderes Objekt ist. Was die new
Operator bewirkt, dass er die __proto__
Eigenschaft, die auf die Funktion prototype
Eigentum. Lesen Sie das noch einmal. Im Grunde geht es um Folgendes:
a1.__proto__ = A.prototype;
Wir sagten, dass A.prototype
ist nichts weiter als ein leeres Objekt (es sei denn, wir ändern es in etwas anderes, bevor wir a1
). Im Grunde also a1.__proto__
weist auf das Gleiche hin A.prototype
auf das das leere Objekt zeigt. Beide verweisen auf dasselbe Objekt, das erstellt wurde, als diese Zeile entstand:
A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}
Nun, es gibt noch etwas, das passiert, wenn var a1 = new A()
Anweisung verarbeitet wird. Grundsätzlich A()
ausgeführt wird und wenn A etwa so aussieht:
var A = function() { this.hey = function() { alert('from A') } };
Das ganze Zeug da drin function() { }
ausführen wird. Wenn Sie die this.hey..
Linie, this
wird geändert in a1
und Sie erhalten dies:
a1.hey = function() { alert('from A') }
Ich werde nicht darauf eingehen, warum this
Änderungen an a1
pero das ist eine großartige Antwort um mehr zu erfahren.
Zusammenfassend lässt sich also sagen, dass Sie, wenn Sie var a1 = new A()
Im Hintergrund laufen 3 Dinge ab:
Ein völlig neues, leeres Objekt wird erstellt und der a1
. a1 = {}
a1.__proto__
Eigenschaft wird zugewiesen, um auf dasselbe zu zeigen wie A.prototype
zeigt auf (ein anderes leeres Objekt {} )
Die Funktion A()
wird ausgeführt mit this
auf das neue, leere Objekt gesetzt, das in Schritt 1 erstellt wurde (lesen Sie die Antwort, auf die ich oben verwiesen habe, um zu erfahren, warum this
Änderungen an a1
)
Lassen Sie uns nun versuchen, ein weiteres Objekt zu erstellen:
var a2 = new A();
Die Schritte 1, 2 und 3 werden wiederholt. Fällt Ihnen etwas auf? Das Schlüsselwort ist wiederholen. Schritt 1: a2
wird ein neues leeres Objekt sein, Schritt 2: sein __proto__
Eigenschaft auf dasselbe hinweist A.prototype
weist auf und vor allem auf Schritt 3 hin: Funktion A()
AGAIN ausgeführt wird, was bedeutet, dass a2
erhalten hey
Eigenschaft, die eine Funktion enthält. a1
y a2
haben zwei SEPARATE Eigenschaften namens hey
die auf 2 getrennte Funktionen verweisen! Wir haben jetzt doppelte Funktionen in zwei verschiedenen Objekten, die dasselbe tun, oops... Sie können sich die Auswirkungen auf den Speicher vorstellen, wenn wir 1000 Objekte haben, die mit new A
Schließlich benötigen Funktionsdeklarationen mehr Speicherplatz als z. B. die Zahl 2. Wie können wir dies also verhindern?
Erinnern Sie sich, warum die __proto__
Eigenschaft bei jedem Objekt vorhanden ist? Wenn Sie also die Eigenschaft yoMan
Eigentum an a1
(die es nicht gibt), seine __proto__
Eigenschaft konsultiert, die, wenn es sich um ein Objekt handelt (und das ist in den meisten Fällen der Fall), überprüft, ob sie die yoMan
und wenn dies nicht der Fall ist, wird das Objekt in der __proto__
usw. Wenn dies der Fall ist, wird dieser Eigenschaftswert übernommen und Ihnen angezeigt.
Also beschloss jemand, diese Tatsache und die Tatsache, dass man bei der Erstellung von a1
, seine __proto__
Eigenschaft zeigt auf dasselbe (leere) Objekt A.prototype
zeigt auf und tut dies:
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
Geil! Wenn Sie jetzt eine a1
durchläuft er erneut alle 3 oben genannten Schritte, und in Schritt 3 tut er nichts, da function A()
hat nichts auszuführen. Und wenn wir es tun:
a1.hey
Sie wird sehen, dass a1
enthält nicht hey
und prüft seine __proto__
Property-Objekt, um zu sehen, ob es diese Eigenschaft hat, was der Fall ist.
Bei diesem Ansatz entfällt der Teil von Schritt 3, bei dem die Funktionen bei jeder neuen Objekterstellung dupliziert werden. Stattdessen a1
y a2
mit einer separaten hey
Eigentum, jetzt hat es KEINER von ihnen. Ich nehme an, das haben Sie inzwischen selbst herausgefunden. Das ist das Schöne daran... wenn Sie verstehen. __proto__
y Function.prototype
werden Fragen wie diese ziemlich offensichtlich sein.
HINWEIS: Manche Leute neigen dazu, die interne Eigenschaft Prototype nicht als __proto__
Ich habe diesen Namen in diesem Beitrag verwendet, um ihn deutlich von den anderen zu unterscheiden. Functional.prototype
Eigentum als zwei verschiedene Dinge.
Eine wirklich gründliche und informative Antwort. Ich habe einige Speichertests mit den oben genannten Objektstrukturen durchgeführt (A.prototype.hey vs. object this.hey) und jeweils 1000 Instanzen erstellt. Der Speicherbedarf für den Objekt-Eigenschafts-Ansatz war etwa 100kb größer im Vergleich zu prototype. Dann fügte ich eine weitere Funktion mit demselben Zweck namens "silly" hinzu, und der Speicherbedarf stieg linear auf 200kb. Nicht signifikant, aber auch keine Peanuts.
Interessanter ist, dass die Prototyp-Methode nur geringfügig langsamer war als die Objekt-Eigenschaftsmethode, die lokal ausgeführt wird. Insgesamt bin ich mir nicht sicher, ob Javascript für die Datenmanipulation von Objekten mit mehr als 10k verwendet werden sollte, so dass es keinen Grund gibt, die Ansätze aufgrund potenzieller Speichereffekte zu ändern. An diesem Punkt sollte die Arbeit auf einen Server verlagert werden.
In den meisten Fällen sind sie im Wesentlichen gleich, aber die zweite Version spart Speicherplatz, weil es nur eine Instanz der Funktion gibt, anstatt eine separate Funktion für jedes Objekt.
Ein Grund für die Verwendung der ersten Form ist der Zugriff auf "private Mitglieder". Zum Beispiel:
var A = function () {
var private_var = ...;
this.x = function () {
return private_var;
};
this.setX = function (new_x) {
private_var = new_x;
};
};
Aufgrund der Scoping-Regeln von Javascript ist private_var für die Funktion verfügbar, die this.x zugewiesen ist, aber nicht außerhalb des Objekts.
Siehe diesen Beitrag: stackoverflow.com/a/1441692/654708 für ein Beispiel, wie man über Prototypen auf private Mitglieder zugreifen kann.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
23 Stimmen
Verwandt: Definieren von Methoden über Prototyp vs. Verwendung im Konstruktor - wirklich ein Leistungsunterschied? und gute Antworten auf Duplikate: Deklaration der Methode im Konstruktor oder am Prototyp , dies vs. Prototyp
0 Stimmen
Das Konzept des Schlüsselworts THIS wird hier explizit erläutert scotch.io/@alZami/verstehen-dieses-in-javascript
3 Stimmen
Die Lektüre "dieses" Threads zeigt, wie schrecklich JS ist und wie sehr seine Prinzipien für viele Entwickler unklar sind. Was genau ist falsch an einfacher zu verstehenden Sprachen? Ich denke, es ist an der Zeit, dass die Entwickler ihre Stimme erheben, um verwirrende Technologien abzulehnen, die keinen oder nur einen geringen Nutzen für das Geschäft oder die Entwicklungsarbeit haben.
1 Stimmen
Am Objekt:
a1.x !== a2.x
; auf Prototyp:a1.x === a2.x