Was passiert, wenn Sie JavaScript sehen, das super() aufruft? In einer abgeleiteten Klasse verwenden Sie super(), um den Konstruktor der übergeordneten Klasse aufzurufen, und super.<methodName>, um auf die Methoden der übergeordneten Klasse zuzugreifen. Dieser Artikel setzt ein grundlegendes Verständnis von Konstruktoren sowie von untergeordneten und übergeordneten Klassen voraus. Wenn Ihnen das völlig neu ist, möchten Sie vielleicht Mozillas Artikel über objektorientiertes JavaScript für Anfänger lesen.
Super ist nicht einzigartig für JavaScript – viele Programmiersprachen, darunter Java und Python, verfügen über ein super()-Schlüsselwort, das eine Referenz auf eine übergeordnete Klasse bereitstellt. JavaScript, im Gegensatz zu Java und Python, basiert nicht auf einem Klassen-Vererbungsmodell. Stattdessen erweitert es das prototypenbasierte Vererbungsmodell von JavaScript, um ein Verhalten zu bieten, das mit der Klassenvererbung konsistent ist.
Lassen Sie uns mehr darüber erfahren und einige Codebeispiele betrachten.
Zuerst eine Aussage aus den Web-Dokumenten von Mozilla für Klassen
JavaScript-Klassen, eingeführt in ECMAScript 2015, sind hauptsächlich syntaktischer Zucker über der bestehenden prototypenbasierten Vererbung von JavaScript. Die Klassensyntax führt kein neues objektorientiertes Vererbungsmodell in JavaScript ein.
Ein Beispiel für eine einfache untergeordnete und übergeordnete Klasse veranschaulicht, was diese Aussage wirklich bedeutet
Siehe den Pen
ZEzggLK von Bailey Jones (@bailey_jones)
auf CodePen.
Mein Beispiel hat zwei Klassen: **fish** und **trout**. Alle Fische haben Informationen über **habitat** (Lebensraum) und **length** (Länge), daher gehören diese Eigenschaften zur Klasse fish. Trout hat zusätzlich eine Eigenschaft für **variety** (Sorte), daher erweitert es fish, um auf den anderen beiden Eigenschaften aufzubauen. Hier sind die Konstruktoren für fish und trout
class fish {
constructor(habitat, length) {
this.habitat = habitat
this.length = length
}
}
class trout extends fish {
constructor(habitat, length, variety) {
super(habitat, length)
this.variety = variety
}
}
Der Konstruktor der Klasse fish definiert habitat und length, und der Konstruktor von trout definiert variety. Ich *muss* super() im Konstruktor von trout aufrufen, sonst erhalte ich einen Referenzfehler, wenn ich versuche, this.variety zu setzen. Das liegt daran, dass ich in Zeile eins der trout-Klasse mit dem Schlüsselwort extends JavaScript gesagt habe, dass trout eine Unterklasse von fish ist. Das bedeutet, dass der `this`-Kontext von trout die in der Klasse fish definierten Eigenschaften und Methoden sowie alle Eigenschaften und Methoden enthält, die trout selbst definiert. Das Aufrufen von super() teilt JavaScript im Wesentlichen mit, was ein Fisch ist, damit es einen `this`-Kontext für trout erstellen kann, der alles von fish enthält, plus alles, was wir für trout definieren werden. Die Klasse fish benötigt kein super(), da ihre "Eltern" nur das JavaScript-Objekt sind. Fish steht bereits am Anfang der prototypenbasierten Vererbungskette, daher ist das Aufrufen von super() nicht notwendig – der `this`-Kontext von fish muss nur Object enthalten, was JavaScript bereits kennt.

Object → fish → trout.Ich habe super(habitat, length) im Konstruktor von trout (referenzierend auf die fish-Klasse) aufgerufen, wodurch alle drei Eigenschaften sofort auf dem `this`-Kontext für trout verfügbar sind. Es gibt tatsächlich einen weiteren Weg, das gleiche Verhalten aus dem Konstruktor von trout zu erzielen. Ich muss super() aufrufen, um einen Referenzfehler zu vermeiden, aber ich muss es nicht "korrekt" mit Parametern aufrufen, die der Konstruktor von fish erwartet. Das liegt daran, dass ich super() nicht verwenden muss, um Werte für die Felder zuzuweisen, die fish erstellt – ich muss nur sicherstellen, dass diese Felder im `this`-Kontext von trout vorhanden sind. Dies ist ein wichtiger Unterschied zwischen JavaScript und einem echten Klassenvererbungsmodell wie Java, wo der folgende Code illegal sein könnte, je nachdem, wie ich die fish-Klasse implementiert habe
class trout extends fish {
constructor(habitat, length, variety) {
super()
this.habitat = habitat
this.length = length
this.variety = variety
}
}
Dieser alternative trout-Konstruktor macht es schwieriger zu erkennen, welche Eigenschaften zu fish und welche zu trout gehören, aber er liefert das gleiche Ergebnis wie das vorherige Beispiel. Der einzige Unterschied ist, dass hier das Aufrufen von super() ohne Parameter die Eigenschaften habitat und length im aktuellen `this`-Kontext erstellt, ohne ihnen etwas zuzuweisen. Wenn ich nach Zeile drei console.log(this) aufrufen würde, würde es {habitat: undefined, length: undefined} ausgeben. Zeilen vier und fünf weisen Werte zu.
Ich kann super() auch außerhalb des Konstruktors von trout verwenden, um auf Methoden der übergeordneten Klasse zuzugreifen. Hier habe ich eine Methode namens renderProperties definiert, die alle Eigenschaften der Klasse in das HTML-Element anzeigt, das ich ihr übergebe. super() ist hier nützlich, da ich möchte, dass meine trout-Klasse eine ähnliche Methode implementiert, die dasselbe tut, plus ein bisschen mehr – sie weist dem Element eine Klasse zu, bevor sie sein HTML aktualisiert. Ich kann die Logik aus der fish-Klasse wiederverwenden, indem ich super.renderProperties() innerhalb der relevanten Klassenfunktion aufrufe.
class fish {
renderProperties(element) {
element.innerHTML = JSON.stringify(this)
}
}
class trout extends fish {
renderPropertiesWithSuper(element) {
element.className="green"
super.renderProperties(element);
}
Der Name, den Sie wählen, ist wichtig. Ich habe meine Methode in der trout-Klasse renderPropertiesWithSuper() genannt, weil ich immer noch die Option haben möchte, trout.renderProperties() aufzurufen, wie sie in der fish-Klasse definiert ist. Wenn ich die Funktion in der trout-Klasse einfach renderProperties genannt hätte, wäre das vollkommen in Ordnung; jedoch könnte ich dann nicht mehr auf beide Funktionen direkt von einer Instanz von trout zugreifen – das Aufrufen von trout.renderProperties würde die in trout definierte Funktion aufrufen. Das ist nicht unbedingt eine nützliche Implementierung – es ist ein argumentativ besseres Muster für eine Funktion, die super auf diese Weise aufruft, die Funktion ihrer Eltern zu überschreiben –, aber es veranschaulicht, wie flexibel JavaScript Ihre Klassen sein lässt.
Es ist durchaus möglich, dieses Beispiel ohne die Schlüsselwörter super() oder extends zu implementieren, die im vorherigen Codebeispiel so hilfreich waren, es ist nur viel weniger bequem. Das ist es, was Mozilla mit "syntaktischem Zucker" meinte. Tatsächlich würde mein vorheriger Code, wenn ich ihn in einen Transpiler wie Babel einfügen würde, um sicherzustellen, dass meine Klassen mit älteren JavaScript-Versionen funktionieren, etwas Ähnliches wie der folgende Code generieren. Der Code hier ist weitgehend gleich, aber Sie werden feststellen, dass ich ohne extends und super() fish und trout als Funktionen definieren und direkt auf ihre Prototypen zugreifen muss. Ich muss auch einige zusätzliche Verdrahtungen in den Zeilen 15, 16 und 17 vornehmen, um trout als Unterklasse von fish zu etablieren und sicherzustellen, dass trout im Konstruktor den richtigen `this`-Kontext übergeben werden kann. Wenn Sie daran interessiert sind, tief in das einzutauchen, was hier vor sich geht, hat Eric Green einen exzellenten Beitrag mit vielen Codebeispielen, wie man Klassen mit und ohne ES2015 erstellt.
Siehe den Pen
wvvdxdd von Bailey Jones (@bailey_jones)
auf CodePen.
Klassen in JavaScript sind eine leistungsstarke Möglichkeit, Funktionalität zu teilen. Klassenkomponenten in React beispielsweise basieren auf ihnen. Wenn Sie jedoch die objektorientierte Programmierung in einer anderen Sprache gewohnt sind, die ein Klassenvererbungsmodell verwendet, kann das Verhalten von JavaScript gelegentlich überraschend sein. Das Erlernen der Grundlagen der prototypenbasierten Vererbung kann Ihnen helfen, die Arbeit mit Klassen in JavaScript zu verstehen.
Schöner Artikel. Ich mag das letzte Beispiel sehr, wenn Sie eine Oberklasse und eine Unterklasse in den Stil des prototypenbasierten Vererbungsmodells übersetzen.
Großartig geschriebener Artikel, und machen Sie so weiter. Der letzte Artikel über anklickbare Elemente in einem SVG hat mir sehr geholfen. Ich würde auch sagen, dass dies nur ein weiterer Schritt ist, um JavaScript zu einem noch erstaunlicheren Werkzeug zum Programmieren zu machen.