Implementation of Inheritance Mechanism in ECMAScript
- Vorherige Seite Beispiel für das Vererbungsmechanismus
- Nächste Seite Advanced JavaScript Tutorial
Implementierung des Vererbungsmechanismus
Um das Vererbungsmechanismus in ECMAScript zu implementieren, können Sie mit der zu vererbenden Basisklasse beginnen. Alle vom Entwickler definierten Klassen können als Basisklasse dienen. Aus Sicherheitsgründen können lokale Klassen und Host-Klassen nicht als Basisklasse verwendet werden, um den öffentlichen Zugriff auf kompilierte browserseitige Code zu verhindern, da dieser für böswillige Angriffe verwendet werden kann.
Nachdem die Basisklasse ausgewählt wurde, kann ihre Unterklasse erstellt werden. Die Verwendung der Basisklasse ist vollständig in Ihrer Hand. Manchmal möchten Sie möglicherweise eine Basisklasse erstellen, die nicht direkt verwendet werden kann, sondern nur allgemeine Funktionen für Unterklassen bereitstellt. In diesem Fall wird die Basisklasse als abstrakte Klasse betrachtet.
Obwohl ECMAScript keine abstrakten Klassen so streng definiert wie andere Sprachen, erstellt es manchmal tatsächlich Klassen, die nicht verwendet werden dürfen. Solche Klassen nennen wir in der Regel abstrakte Klassen.
Die vom Benutzer erstellten Unterklassen übernehmen alle Eigenschaften und Methoden der Oberklasse, einschließlich der Implementierung von Konstruktoren und Methoden. Beachten Sie, dass alle Eigenschaften und Methoden öffentlich sind, daher können die Unterklassen diese Methoden direkt aufrufen. Die Unterklassen können auch neue Eigenschaften und Methoden hinzufügen, die in der Oberklasse nicht vorhanden sind, und die Eigenschaften und Methoden der Oberklasse überschreiben.
Wege der Vererbung
Wie andere Funktionen auch gibt es mehrere Möglichkeiten, wie ECMAScript die Vererbung implementiert. Das liegt daran, dass das Vererbungsmechanismus in JavaScript nicht klar definiert ist, sondern durch Imitation realisiert wird. Das bedeutet, dass nicht alle Details der Vererbung vollständig vom Interpreter bearbeitet werden. Als Entwickler haben Sie das Recht, die am besten geeignete Vererbungsweise zu wählen.
Nachfolgend werden einige spezifische Vererbungsweisen vorgestellt.
Object Masquerading
Bei der Konzeption des ursprünglichen ECMAScript war es nie beabsichtigt, Object Masquerading (Objektverkleidung) zu entwerfen. Es entwickelte sich erst, als Entwickler begannen zu verstehen, wie Funktionen funktionieren, insbesondere wie das Schlüsselwort this in einer Funktionsumgebung verwendet wird.
Das Prinzip ist wie folgt: Der Konstruktor verwendet den Schlüsselwort this, um alle Attribute und Methoden zuzuweisen (d.h. auf den Weg der Konstruktordeklaration der Klasse). Da der Konstruktor nur eine Funktion ist, kann der Konstruktor von ClassA als Methode von ClassB verwendet werden und wird aufgerufen. ClassB erhält dann die Attribute und Methoden, die im Konstruktor von ClassA definiert sind. Zum Beispiel können ClassA und ClassB wie folgt definiert werden:
function ClassA(sColor) { this.color = sColor; this.sayColor = function () { alert(this.color); }; } function ClassB(sColor) { }
Denken Sie daran? Das Schlüsselwort this bezieht sich auf das Objekt, das der aktuelle Konstruktor erstellt. Allerdings verweist this in diesem Fall auf das Objekt, dem das gehört. Der Prinzip ist, ClassA als reguläre Funktion zu verwenden, um das Vererbungsmechanismus zu etablieren, anstatt als Konstruktor. So kann das Vererbungsmechanismus mit dem Konstruktor ClassB realisiert werden:
function ClassB(sColor) { this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; }
In diesem Code wird der Methode newMethod von ClassA zugewiesen (denken Sie daran, dass der Funktionsname nur ein Zeiger auf denselben ist). Diese Methode wird dann aufgerufen und ihr wird der Parameter sColor der Konstruktor von ClassB übergeben. Das letzte Zeile des Codes löscht den Verweis auf ClassA, sodass diese in Zukunft nicht mehr aufgerufen werden kann.
Alle neuen Attribute und Methoden müssen nach dem Entfernen der neuen Methode definiert werden. Andernfalls könnte es zu einer Überschreibung der entsprechenden Attribute und Methoden der Oberklasse kommen:
function ClassB(sColor, sName) { this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; this.name = sName; this.sayName = function () { alert(this.name); }; }
Um zu beweisen, dass der vorherige Code funktioniert, kann der folgende Beispiel ausgeführt werden:
var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); // Ausgabe "blue" objB.sayColor(); // Ausgabe "red" objB.sayName(); // Ausgabe "John"
Object Masquerading kann Mehrfachvererbung realisieren
Interessanterweise unterstützt das Object Masquerading (Objektverkleidung) Multiple Inheritance. Das bedeutet, dass eine Klasse mehrere Oberklassen erben kann. Das Mechanismus der Mehrfachvererbung, wie es in UML dargestellt wird, ist in der folgenden Abbildung zu sehen:

Zum Beispiel, wenn es zwei Klassen ClassX und ClassY gibt, kann ClassZ diese beiden Klassen erben, indem der folgende Code verwendet wird:
function ClassZ() { this.newMethod = ClassX; this.newMethod(); delete this.newMethod; this.newMethod = ClassY; this.newMethod(); delete this.newMethod; }
Es gibt einen Nachteil, wenn zwei Klassen ClassX und ClassY über gleichnamige Eigenschaften oder Methoden verfügen, hat ClassY eine höhere Priorität, weil sie von der hinteren Klasse vererbt wird. Abgesehen von diesem kleinen Problem lässt sich das Mechanismus der Mehrfachvererbung durch Objektschmuggel leicht umsetzen.
Aufgrund der Beliebtheit dieser Vererbungsmethode haben die ECMAScript-Drittelversionen zwei Methoden dem Function-Objekt hinzugefügt, nämlich call() und apply().
call() Methode
call() Methode ist die ähnlichste Methode zu den klassischen Methoden des Objektschmuggels. Der erste Parameter wird als das this-Objekt verwendet. Die anderen Parameter werden direkt an die Funktion selbst übergeben. Zum Beispiel:
function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.call(obj, "The color is ", "a very nice color indeed.");
In diesem Beispiel ist die Funktion sayColor() außerhalb des Objekts definiert, auch wenn sie nicht zu einem Objekt gehört, kann das Schlüsselwort this verwendet werden. Die Eigenschaft color des Objekts obj ist gleich blue. Beim Aufruf der Methode call() ist der erste Parameter obj, was bedeutet, dass dem Schlüsselwort this in der Funktion sayColor() der Wert obj zugewiesen werden soll. Die zweiten und dritten Parameter sind Strings. Sie entsprechen den Parametern sPrefix und sSuffix in der Funktion sayColor(). Die letztendlich generierte Nachricht "The color is blue, a very nice color indeed." wird angezeigt.
Um diese Methode mit Methoden der Vererbungsmechanismen zu verwenden, ersetzen Sie einfach die ersten drei Zeilen mit Zuweisungen, Aufrufen und Löschungen:
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.call(this, sColor); this.name = sName; this.sayName = function () { alert(this.name); }; }
Hier müssen wir sicherstellen, dass das Schlüsselwort this in ClassA dem neu erstellten ClassB-Objekt entspricht, daher ist this der erste Parameter. Der zweite Parameter sColor ist für beide Klassen einzigartig.
apply() Methode
apply() Methode hat zwei Parameter, die als das this-Objekt und das Array der an die Funktion zu übergebenen Parameter dienen. Zum Beispiel:
function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));
Dieser Beispiel ist mit dem vorherigen Beispiel identisch, nur dass jetzt die Methode apply() aufgerufen wird. Beim Aufruf der Methode apply() ist der erste Parameter immer obj, was bedeutet, dass dem Schlüsselwort this in der Funktion sayColor() der Wert obj zugewiesen werden sollte. Der zweite Parameter ist ein Array aus zwei Strings, das mit den Parametern sPrefix und sSuffix in der Funktion sayColor() übereinstimmt, und die generierte Nachricht "The color is blue, a very nice color indeed." wird angezeigt.
Diese Methode wird auch verwendet, um die drei Zeilen zu ersetzen, die die Zuweisung, den Aufruf und das Löschen der neuen Methode enthalten:
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, new Array(sColor)); this.name = sName; this.sayName = function () { alert(this.name); }; }
Gleichzeitig ist der erste Parameter immer this, der zweite Parameter ist ein Array mit nur einem Wert color. Das gesamte arguments-Objekt der ClassB kann als zweiter Parameter an die Methode apply() übergeben werden:
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, arguments); this.name = sName; this.sayName = function () { alert(this.name); }; }
Natürlich kann ein Parameterobjekt nur dann übergeben werden, wenn die Reihenfolge der Parameter in der Superklasse mit der Reihenfolge der Parameter in der Unterklasse vollständig übereinstimmt. Wenn dies nicht der Fall ist, muss ein separates Array erstellt werden, in dem die Parameter in der richtigen Reihenfolge platziert werden. Darüber hinaus kann auch die Methode call() verwendet werden.
Prototype-Chain (prototype chaining)
Diese Form der Vererbung wurde ursprünglich in ECMAScript für das prototype-Chain konzipiert. Im vorherigen Kapitel wurde die Definition der Klasse mit dem Prototypen-Modus vorgestellt. Das prototype-Chain erweitert diese Methode und realisiert den Vererbungsmechanismus auf eine interessante Weise.
Wie im vorherigen Kapitel gelernt, ist das prototype-Objekt ein Muster, auf dem alle zu instanziierenden Objekte basieren. Kurz gesagt, jede Eigenschaft und jeder Methodenaufruf des prototype-Objekts wird an alle Instanzen dieser Klasse weitergegeben. Die prototype-Kette nutzt diese Funktion, um das Vererbungsmechanismus zu realisieren.
Wenn die Klasse mit dem Prototypen-Modus in dem obigen Beispiel neu definiert wird, nehmen sie die folgende Form an:
function ClassA() { } ClassA.prototype.color = "blue"; ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB() { } ClassB.prototype = new ClassA();
Das Wunderbare am Prototypen-Verfahren liegt in der hervorgehobenen blauen Zeile. Hier wird das prototype-Attribut von ClassB auf eine Instanz von ClassA gesetzt. Das ist interessant, weil man alle Eigenschaften und Methoden von ClassA haben möchte, aber nicht unbedingt alle einzeln in das prototype-Attribut von ClassB einfügen möchte. Gibt es eine bessere Methode als das Zuweisen einer Instanz von ClassA an das prototype-Attribut?}
Hinweis:Der Konstruktor von ClassA wird aufgerufen, ohne dass Parameter an ihn übergeben werden. Dies ist im Prototypen-Chain das Standardverhalten. Stellen Sie sicher, dass der Konstruktor keine Parameter hat.
Ähnlich wie bei der Objektverkleidung müssen alle Eigenschaften und Methoden der Unterklasse nach der Zuweisung der prototype-Eigenschaft auftauchen, da alle Methoden, die vor der Zuweisung vorhanden sind, gelöscht werden. Warum? Weil die prototype-Eigenschaft durch einen neuen Objekt ersetzt wird, und das ursprüngliche Objekt, das neue Methoden hinzufügte, wird zerstört. Daher lautet der Code, um der Klasse ClassB die Eigenschaft name und die Methode sayName() hinzuzufügen, wie folgt:
function ClassB() { } ClassB.prototype = new ClassA(); ClassB.prototype.name = ""; ClassB.prototype.sayName = function () { alert(this.name); };
Dieses Stück Code kann durch Ausführen des folgenden Beispiels getestet werden:
var objA = new ClassA(); var objB = new ClassB(); objA.color = "blue"; objB.color = "red"; objB.name = "John"; objA.sayColor(); objB.sayColor(); objB.sayName();
Darüber hinaus funktioniert der instanceof-Operator im Prototypen-Chain auch auf eine einzigartige Weise. Für alle Instanzen von ClassB gibt instanceof sowohl ClassA als auch ClassB zurück. Zum Beispiel:
var objB = new ClassB(); alert(objB instanceof ClassA); // Ausgabe "true" alert(objB instanceof ClassB); // Ausgabe "true"
In der Welt der schwachen Typen von ECMAScript ist dies ein äußerst nützliches Werkzeug, aber es kann nicht verwendet werden, wenn das Objekt-Verkleidung verwendet wird.
Der Nachteil des Prototypen-Chains ist, dass es keine Mehrfachvererbung unterstützt. Denken Sie daran, dass das Prototypen-Chain die prototype-Eigenschaft der Klasse mit einem anderen Typ des Objekts überschreibt.
Mischungsmethode
Diese Vererbungsart verwendet den Konstruktor zur Definition der Klasse und nicht irgendeinen Prototypen. Das Hauptproblem des Objekt-Verkleidens ist, dass der Konstruktor verwendet werden muss, was nicht die beste Wahl ist. Wenn jedoch das Prototypen-Chain verwendet wird, kann kein mit Parametern versehener Konstruktor verwendet werden. Wie wählen Entwickler? Die Antwort ist einfach: Beide verwenden.
Im vorherigen Kapitel haben wir erklärt, dass die beste Methode zur Erstellung von Klassen die Verwendung von Konstruktoren zur Definition von Eigenschaften und dem Prototypen zur Definition von Methoden ist. Diese Methode ist auch für das Vererbungsmechanismus geeignet, indem die Eigenschaften der Konstruktoren durch Objekte nachgeahmt und die Methoden des prototype-Objekts über das Prototypen-Chain vererbt werden. Mit diesen beiden Methoden wird der vorangegangene Beispiel wie folgt neu geschrieben:
function ClassA(sColor) { this.color = sColor; } ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB(sColor, sName) { ClassA.call(this, sColor); this.name = sName; } ClassB.prototype = new ClassA(); ClassB.prototype.sayName = function () { alert(this.name); };
In diesem Beispiel wird das Vererbungsmechanismus durch zwei hervorgehobene blau markierte Zeilen implementiert. In der ersten hervorgehobenen Zeile wird in der Konstruktorfunktion von ClassB die sColor-Eigenschaft der ClassA-Klasse durch ein Objekt nachgeahmt. In der zweiten hervorgehobenen Zeile werden Methoden der ClassA-Klasse über das Prototypen-Chain vererbt. Da diese Mischung das Prototypen-Chain verwendet, funktioniert der instanceof-Operator immer noch korrekt.
Der folgende Beispiel testet diesen Code:
var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); // Ausgabe "blue" objB.sayColor(); // Ausgabe "red" objB.sayName(); // Ausgabe "John"
- Vorherige Seite Beispiel für das Vererbungsmechanismus
- Nächste Seite Advanced JavaScript Tutorial