vererbung - prototype chain javascript




Verwendung von 'Prototyp' im Vergleich zu 'diesem' in JavaScript? (10)

Was ist der Unterschied zwischen

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

und

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

Das erste Beispiel ändert nur die Schnittstelle für dieses Objekt. Das zweite Beispiel ändert die Schnittstelle für alle Objekte dieser Klasse.


Das ultimative Problem bei der Verwendung this prototype ist, dass beim Überschreiben einer Methode der Konstruktor der Basisklasse immer noch auf die überschriebene Methode verweist. Bedenken Sie:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

gegen:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

Wenn Sie denken, dass dies kein Problem ist, dann hängt es davon ab, ob Sie ohne private Variablen leben können und ob Sie erfahren genug sind, um ein Leck zu erkennen, wenn Sie eines sehen. Außerdem ist es unpraktisch, die Konstruktorlogik nach den Methodendefinitionen zu setzen.

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

gegen:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1

Ich glaube, dass @ Matthew Crumley hat Recht. Sie sind funktional , wenn nicht strukturell gleichwertig. Wenn Sie mithilfe von Firebug die Objekte anzeigen, die mit new , können Sie sehen, dass sie identisch sind. Meine Präferenz wäre jedoch die folgende. Ich vermute, dass es eher so aussieht, wie ich es in C # / Java gewohnt bin. Definieren Sie die Klasse, definieren Sie die Felder, den Konstruktor und die Methoden.

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

EDIT Didn't mean to imply that the scope of the variable was private, I was just trying to illustrate how I define my classes in javascript. Variable name has been changed to reflect this.


In den meisten Fällen sind sie im Wesentlichen gleich, aber die zweite Version spart Speicher, da es nur eine Instanz der Funktion anstelle einer separaten Funktion für jedes Objekt gibt.

Ein Grund, das erste Formular zu verwenden, ist der Zugriff auf "private Mitglieder". Beispielsweise:

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 zugeordnet ist, aber nicht außerhalb des Objekts.


Lassen Sie mich Ihnen eine umfassendere Antwort geben, die ich während eines JavaScript-Trainingskurses gelernt habe.

Die meisten Antworten erwähnten den Unterschied bereits, dh beim Prototyping wird die Funktion mit allen (zukünftigen) Instanzen geteilt. Während das Deklarieren der Funktion in der Klasse eine Kopie für jede Instanz erstellt.

Im Allgemeinen gibt es kein Richtig oder Falsch, es ist mehr eine Frage des Geschmacks oder eine Designentscheidung, die von Ihren Anforderungen abhängt. Der Prototyp ist jedoch die Technik, die verwendet wird, um objektorientiert zu entwickeln, wie ich hoffe, Sie werden am Ende dieser Antwort sehen.

Sie haben zwei Muster in Ihrer Frage angezeigt. Ich werde versuchen, zwei weitere zu erklären und versuchen, die Unterschiede zu erklären, wenn relevant. Fühlen Sie sich frei zu bearbeiten / zu erweitern. In allen Beispielen handelt es sich um ein Autoobjekt, das einen Standort hat und sich bewegen kann.

Objekt Dekorator Muster

Nicht sicher, ob dieses Muster heute noch relevant ist, aber es existiert. Und es ist gut, davon zu wissen. Sie übergeben einfach ein Objekt und eine Eigenschaft an die Decorator-Funktion. Der Decorator gibt das Objekt mit Eigenschaft und Methode zurück.

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

Funktionsklassen

Eine Funktion in JavaScript ist ein spezialisiertes Objekt. Zusätzlich zum Aufruf kann eine Funktion Eigenschaften wie jedes andere Objekt speichern.

In diesem Fall ist Car eine Funktion ( auch Think- Objekt ), die wie gewohnt aufgerufen werden kann. Es hat eine Eigenschaft methods (das ist ein Objekt mit einer move Funktion). Wenn Car aufgerufen wird, wird die extend Funktion aufgerufen, die etwas magisch macht und die Car Funktion (think-Objekt) um die in Methoden definierten methods .

Dieses Beispiel kommt, obwohl es anders ist, dem ersten Beispiel in der Frage am nächsten.

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

Prototypische Klassen

Die ersten beiden Muster ermöglichen eine Erörterung der Verwendung von Techniken zum Definieren gemeinsamer Methoden oder zum Verwenden von Methoden, die inline im Körper des Konstruktors definiert sind. In beiden Fällen hat jede Instanz ihre eigene move .

Das prototypische Muster eignet sich nicht für die gleiche Untersuchung, da das Teilen der Funktionen über eine Prototyp-Delegierung das eigentliche Ziel für das prototypische Muster ist. Wie andere darauf hingewiesen haben, wird erwartet, dass es einen besseren Speicherbedarf hat.

Es gibt jedoch einen interessanten Punkt: Jedes prototype hat einen Convenience-Eigenschaftskonstruktor, der auf die Funktion (Think-Objekt) verweist, an die er angehängt wurde.

Zu den letzten drei Zeilen:

In diesem Beispiel verlinkt Car auf das prototype Objekt, das über constructor mit Car selbst verbunden ist, dh Car.prototype.constructor ist Car selbst. Dadurch können Sie herausfinden, welche Konstruktorfunktion ein bestimmtes Objekt erstellt hat.

amy.constructor schlägt fehl und wird daher an Car.prototype delegiert, die die Eigenschaft constructor besitzt. Und amy.constructor ist Car .

Außerdem ist amy eine instanceof Car . Der instanceof Operator prüft, ob das Prototypobjekt des rechten Operanden ( Car ) irgendwo in der Prototypkette des linken Operanden ( amy ) gefunden werden kann.

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

Einige Entwickler können am Anfang verwirrt sein. Siehe folgendes Beispiel:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

Der Operator instanceof gibt false , da Dog Prototyp nirgends in der Prototypkette von fido gefunden werden kann. fido ist ein einfaches Objekt, das mit einem Objektliteral erstellt wird, dh es delegiert nur an Object.prototype .

Pseudoklassische Muster

Dies ist wirklich nur eine andere Form des Prototypmusters in vereinfachter Form und vertrauter, um diejenigen zu tun, die zum Beispiel in Java programmieren, da es den new Konstruktor verwendet.

Es tut das selbe wie im prototypischen Muster wirklich, es ist nur syntaktischer Zuckerüberhang des prototypischen Musters.

Der Hauptunterschied besteht jedoch darin, dass Optimierungen in JavaScript-Engines implementiert sind, die nur bei Verwendung des Pseudoklassikmusters angewendet werden. Stellen Sie sich das Pseudoklassische Muster als wahrscheinlich schnellere Version des prototypischen Musters vor; Die Objektbeziehungen in beiden Beispielen sind gleich.

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

Schließlich sollte es nicht zu schwierig sein zu erkennen, wie objektorientierte Programmierung durchgeführt werden kann. Es gibt zwei Abschnitte.

Ein Abschnitt, der allgemeine Eigenschaften / Methoden im Prototyp (Kette) definiert.

Und noch ein Abschnitt, in dem Sie die Definitionen einfügen, die die Objekte voneinander unterscheiden ( loc Variable in den Beispielen).

Dies ermöglicht uns, Konzepte wie Superklasse oder Unterklasse in JavaScript anzuwenden.

Fühlen Sie sich frei, hinzuzufügen oder zu bearbeiten. Noch einmal abgeschlossen, könnte ich das vielleicht zu einem Community-Wiki machen.


Nehmen Sie diese 2 Beispiele:

var A = function() { this.hey = function() { alert('from A') } };

gegen

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

Die meisten Leute hier (besonders die Top-Antworten) versuchten zu erklären, wie sie anders sind, ohne zu erklären, warum. Ich denke, das ist falsch und wenn Sie zuerst die Grundlagen verstehen, wird der Unterschied offensichtlich. Lassen Sie uns zuerst versuchen, die Grundlagen zu erklären ...

a) Eine Funktion ist ein Objekt in JavaScript. JEDES Objekt in JavaScript erhält eine interne Eigenschaft (das heißt, Sie können nicht wie andere Eigenschaften darauf zugreifen, außer vielleicht in Browsern wie Chrome), oft als __proto__ (Sie können anyObject.__proto__ in Chrome anyObject.__proto__ zu sehen, worauf es verweist. Das ist nur das, eine Eigenschaft, nichts mehr. Eine Eigenschaft in JavaScript = eine Variable in einem Objekt, nichts mehr. Was tun Variablen? Sie zeigen auf Dinge.

Worauf deutet diese __proto__ Eigenschaft hin? Nun, normalerweise ein anderes Objekt (wir erklären später warum). Die einzige Möglichkeit, JavaScript zu erzwingen, dass die __proto__ Eigenschaft NICHT auf ein anderes Objekt zeigt, ist var newObj = Object.create(null) . Selbst wenn Sie dies tun, existiert die __proto__ Eigenschaft STILL als Eigenschaft des Objekts. Sie zeigt jedoch nicht auf ein anderes Objekt, sondern auf null .

Hier werden 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 sich vollständig von der Eigenschaft __proto__ . In unserem Beispiel hat 'A' nun zwei Eigenschaften namens 'Prototyp' und ' __proto__ . Dies ist eine große Verwirrung für die Menschen. prototype und __proto__ Eigenschaften sind in keiner Weise verwandt, sie sind separate Dinge, die auf separate Werte verweisen.

Sie fragen sich vielleicht: Warum hat JavaScript die Eigenschaft __proto__ auf jedem einzelnen 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 von __proto__ verwiesen wird, __proto__ zu sehen, ob es das Objekt hat. Wenn es nicht vorhanden ist, dann schaut es sich die Eigenschaft __proto__ dieses Objekts an und so weiter ... bis die Kette endet. Also der Name Prototyp Kette . Wenn __proto__ nicht auf ein Objekt zeigt und statt dessen auf null , erkennt JavaScript das und gibt Ihnen undefined für die Eigenschaft zurück.

Sie fragen sich vielleicht auch, warum JavaScript eine Eigenschaft namens prototype für eine Funktion erstellt, wenn Sie die Funktion definieren? Weil es versucht, dich zu täuschen, täusche dich, dass es wie klassenbasierte Sprachen funktioniert.

Lassen Sie uns mit unserem Beispiel fortfahren und ein "Objekt" aus A erstellen:

var a1 = new A();

Es passiert etwas im Hintergrund, als das 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() tat etwas ZUSÄTZLICHES im Hintergrund. Das new Schlüsselwort hat ein neues Objekt erstellt, das nun auf a1 verweist und das Objekt leer ist. Hier passiert was zusätzlich:

Wir haben gesagt, dass es für jede Funktionsdefinition eine neue Eigenschaft namens prototype (auf die Sie anders als mit der Eigenschaft __proto__ ). Nun, diese Eigenschaft wird jetzt verwendet.

Wir sind jetzt an dem Punkt, wo wir ein frisch gebackenes leeres a1 Objekt haben. Wir haben gesagt, dass alle Objekte in JavaScript eine interne __proto__ die auf etwas zeigt ( a1 hat es auch), egal, ob es null oder ein anderes Objekt ist. Der new Operator bewirkt, dass __proto__ Eigenschaft __proto__ auf die prototype der Funktion __proto__ . Lies das noch einmal. Es ist im Grunde das:

a1.__proto__ = A.prototype;

Wir haben gesagt, dass A.prototype nichts anderes als ein leeres Objekt ist (außer wir ändern es vor der Definition von a1 in etwas anderes). Nun zeigt im Grunde a1.__proto__ auf dasselbe, worauf A.prototype zeigt, nämlich das leere Objekt. Sie zeigen beide auf das gleiche Objekt, das erzeugt wurde, als diese Linie passierte:

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}

Nun passiert noch etwas, wenn var a1 = new A() Anweisung verarbeitet wird. Grundsätzlich wird A() ausgeführt und wenn A etwa so ist:

var A = function() { this.hey = function() { alert('from A') } };

All das Zeug innerhalb von function() { } wird ausgeführt. Wenn Sie die this.hey.. Zeile erreichen, wird this in a1 geändert und Sie erhalten this.hey.. :

a1.hey = function() { alert('from A') }

Ich werde nicht erklären, warum this zu a1 geändert wird, aber dies ist eine großartige Antwort , um mehr zu erfahren.

Zusammengefasst gesagt, wenn Sie "var a1 = new A ()" machen, passieren im Hintergrund drei Dinge:

  1. Ein völlig neues leeres Objekt wird erstellt und a1 zugewiesen. a1 = {}
  2. a1.__proto__ Eigenschaft wird zugewiesen, um auf denselben Punkt zu verweisen, auf den A.prototype zeigt (ein anderes leeres Objekt {})

  3. Die Funktion A() wird mit this Set auf das neue, leere Objekt ausgeführt, das in Schritt 1 erstellt wurde (lesen Sie die Antwort, auf die oben Bezug genommen wurde, warum sich this in a1 ändert).

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 Wiederholung. Schritt 1: a2 wird ein neues leeres Objekt sein, Schritt 2: seine __proto__ Eigenschaft wird auf das gleiche zeigen, auf das A.prototype zeigt und vor allem auf Schritt 3: function A() wird AGAIN ausgeführt, was bedeutet, dass a2 hey bekommt Eigenschaft, die eine Funktion enthält. a1 und a2 haben zwei SEPARATE Eigenschaften mit dem Namen hey die auf 2 SEPARATE Funktionen zeigen! Wir haben nun doppelte Funktionen in denselben zwei verschiedenen Objekten, die dasselbe tun, oops ... Sie können sich die Speicherauswirkungen vorstellen, wenn wir 1000 Objekte mit new A , nachdem alle Funktionsdeklarationen mehr Speicher benötigen als etwa die Zahl 2 Wie verhindern wir das?

Merken Sie sich, warum die Eigenschaft __proto__ für jedes Objekt existiert? Wenn Sie also die yoMan Eigenschaft auf a1 (die nicht existiert) __proto__ , wird ihre __proto__ Wenn es ein Objekt ist (und in den meisten Fällen ist es das), prüft es, ob es yoMan enthält und ob es es enthält nicht, es wird das __proto__ des __proto__ usw. konsultieren. Wenn dies der Fall ist, wird es diesen Eigenschaftswert übernehmen und Ihnen anzeigen.

Also hat jemand entschieden, diese Tatsache + die Tatsache zu verwenden, dass wenn Sie a1 erstellen, seine __proto__ Eigenschaft auf das gleiche (leere) Objekt zeigt, auf das A.prototype zeigt und dies tut:

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

Cool! Wenn Sie jetzt a1 erstellen, durchläuft es erneut alle drei obigen Schritte und in Schritt 3 wird nichts ausgeführt, da die function A() nichts auszuführen hat. Und wenn wir es tun:

a1.hey

Es wird sehen, dass a1 nicht hey enthält und es wird sein __proto__ Eigenschaftenobjekt überprüfen, um zu sehen, ob es es hat, was der Fall ist.

Mit diesem Ansatz eliminieren wir den Teil von Schritt 3, in dem Funktionen bei jeder neuen Objekterstellung dupliziert werden. Anstelle von a1 und a2 , die eine separate hey Eigenschaft haben, hat jetzt KEINE von ihnen es. Was, denke ich, hast du inzwischen selbst herausgefunden. Das ist das __proto__ ... Wenn Sie __proto__ und Function.prototype verstehen, werden Fragen wie diese ziemlich offensichtlich sein.

Hinweis: einige Leute neigen dazu, die interne Eigenschaft Prototyp nicht als __proto__ . Ich habe diesen Namen über den Post verwendet, um ihn deutlich von der Eigenschaft Functional.prototype als zwei verschiedene Dinge zu unterscheiden.


Was ist der Unterschied? => Eine Menge.

Ich denke, this Version wird verwendet, um die Kapselung, dh das Verstecken von Daten, zu ermöglichen. Es hilft, private Variablen zu manipulieren.

Betrachten wir das folgende Beispiel:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

Jetzt kann die prototype wie folgt angewendet werden:

Verschiedene Erwachsene haben unterschiedliche Altersstufen, aber alle Erwachsenen haben die gleichen Rechte.
Also fügen wir es eher mithilfe von Prototypen hinzu.

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

Schauen wir uns jetzt die Implementierung an.

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

Hoffe das hilft.


Wie andere in der ersten Version gesagt haben, führt die Verwendung von "this" dazu, dass jede Instanz der Klasse A ihre eigene unabhängige Kopie der Funktionsmethode "x" hat. Während die Verwendung von "Prototyp" bedeutet, dass jede Instanz der Klasse A die gleiche Kopie der Methode "x" verwendet.

Hier ist ein Code, um diesen feinen Unterschied zu zeigen:

// 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, die eine oder die andere Methode zu wählen. Meine Probe soll nur den Unterschied deutlich machen.


I know this has been answered to death but I'd like to show an actual example of speed differences.

Here we're creating 2,000,000 new objects with a print method in Chrome. We're storing every object in an array. Putting print on the prototype takes about 1/2 as long.


Think about statically typed language, things on prototype are static and things on this are instance related.





this