832 Stimmen

Verwendung von 'prototype' vs. 'this' in JavaScript?

Was ist der Unterschied zwischen

var A = function () {
    this.x = function () {
        //do something
    };
};

et

var A = function () { };
A.prototype.x = function () {
    //do something
};

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.

479voto

keparo Punkte 32084

Die Beispiele haben sehr unterschiedliche Ergebnisse.

Bevor Sie sich die Unterschiede ansehen, sollten Sie Folgendes beachten:

  • Ein Konstruktor ist Prototyp bietet eine Möglichkeit, Methoden und Werte zwischen Instanzen über die private Instanz [[Prototype]] Eigentum.
  • Eine Funktion ist diese wird durch die Art des Funktionsaufrufs oder durch die Verwendung von binden (hier nicht behandelt). Wenn eine Funktion auf einem Objekt aufgerufen wird (z. B. 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.
  • JavaScript ist eine objektorientierte Sprache, d. h. die meisten Werte sind Objekte, einschließlich Funktionen. (Zeichenketten, Zahlen und Boolesche Werte sind pas Objekte).

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.

0 Stimmen

Im vierten Fall gibt die Funktion nichts zurück, ist das beabsichtigt?

4 Stimmen

Ist es nicht eigentlich so, dass jeder 機能 hat einen Prototyp ( no jede )? Zum Beispiel wird Folgendes zurückgegeben undefined : ({}).prototype .

2 Stimmen

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.

249voto

Benry Punkte 5228

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.

5 Stimmen

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

19 Stimmen

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.

4 Stimmen

Es ist eine statische Methode! :D

175voto

daremkd Punkte 7884

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:

  1. Ein völlig neues, leeres Objekt wird erstellt und der a1 . a1 = {}

  2. a1.__proto__ Eigenschaft wird zugewiesen, um auf dasselbe zu zeigen wie A.prototype zeigt auf (ein anderes leeres Objekt {} )

  3. 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.

2 Stimmen

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.

0 Stimmen

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.

1 Stimmen

Der Punkt ist __proto__ y .prototype sind völlig unterschiedliche Dinge.

60voto

Matthew Crumley Punkte 98564

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.

1 Stimmen

Siehe diesen Beitrag: stackoverflow.com/a/1441692/654708 für ein Beispiel, wie man über Prototypen auf private Mitglieder zugreifen kann.

0 Stimmen

@GFoley83 diese Antwort tut no zeigen, dass - die Prototyp-Methoden nur auf die "öffentlichen" Eigenschaften des gegebenen Objekts zugreifen können. Nur die privilegierten Methoden (nicht auf den Prototyp) können auf die privaten Mitglieder zugreifen.

28voto

Glenn Punkte 7588

Im ersten Beispiel wird nur die Schnittstelle für dieses Objekt geändert. Das zweite Beispiel ändert die Schnittstelle für alle Objekte dieser Klasse.

1 Stimmen

Beide machen die Funktion x für alle Objekte zur Verfügung, deren Prototyp eine neue Instanz von A zugewiesen wird: function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;

CodeJaeger.com

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.

Powered by:

X