Get to know MDN better
Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.
Diese Funktion ist gut etabliert und funktioniert auf vielen Geräten und in vielen Browserversionen. Sie ist seit März 2016 browserübergreifend verfügbar.
Das extends Schlüsselwort wird in Klassendeklarationen oder Klassenexpressionen verwendet, um eine Klasse zu erstellen, die eine Unterklasse einer anderen Klasse ist.
Ein Ausdruck, der zu einer Konstruktorfunktion (einschließlich einer Klasse) oder null auswertet.
Das extends Schlüsselwort kann zum Erstellen von Unterklassen für benutzerdefinierte Klassen sowie für eingebaute Objekte verwendet werden.
Jeder Konstruktor, der mit new aufgerufen werden kann und die prototype Eigenschaft hat, kann Kandidat für die Elternklasse sein. Beide Bedingungen müssen erfüllt sein – zum Beispiel können gebundene Funktionen und Proxy konstruiert werden, aber sie haben keine prototype Eigenschaft, also können sie nicht unterklassifiziert werden.
Die prototype Eigenschaft der ParentClass muss ein Object oder null sein, aber in der Praxis werden Sie sich selten darum kümmern, da ein nicht-objekt-orientiertes prototype sich ohnehin nicht wie erwartet verhält. (Es wird vom new Operator ignoriert.)
extends legt das Prototyp für sowohl ChildClass als auch ChildClass.prototype fest.
| extends Klausel fehlt | Function.prototype | Object.prototype |
| extends null | Function.prototype | null |
| extends ParentClass | ParentClass | ParentClass.prototype |
Die rechte Seite von extends muss kein Bezeichner sein. Sie können jeden Ausdruck verwenden, der zu einem Konstruktor auswertet. Dies ist oft nützlich, um Mixins zu erstellen. Der this Wert im extends Ausdruck ist das this, das die Klassendefinition umgibt, und das Verweisen auf den Klassennamen führt zu einem ReferenceError, da die Klasse noch nicht initialisiert ist. await und yield funktionieren erwartungsgemäß in diesem Ausdruck.
Während die Basisklasse alles von ihrem Konstruktor zurückgeben kann, muss die abgeleitete Klasse ein Objekt oder undefined zurückgeben, sonst wird ein TypeError ausgelöst.
Wenn der Elternklassenkonstruktor ein Objekt zurückgibt, wird dieses Objekt als this Wert für die abgeleitete Klasse verwendet, wenn diese weiter Klassenfelder initialisiert. Dieser Trick wird als "Rückgabeveränderung" bezeichnet und ermöglicht es, dass die Felder einer abgeleiteten Klasse (einschließlich der privaten) auf nicht verwandten Objekten definiert werden.
Warnung: Das Standardkomitee ist mittlerweile der Ansicht, dass der eingebaute Unterklassifizierungsmechanismus in früheren Spezifikationsversionen überentwickelt ist und nicht unerhebliche Leistungs- und Sicherheitsprobleme verursacht. Neue eingebaute Methoden berücksichtigen Unterklassen weniger, und Implementierer von Engines untersuchen, ob bestimmte Mechanismen der Unterklassifizierung entfernt werden sollen. Ziehen Sie in Betracht, Zusammensetzung anstelle von Vererbung zu verwenden, wenn Sie eingebaute Klassen erweitern.
Hier sind einige Dinge, die Sie erwarten können, wenn Sie eine Klasse erweitern:
Diese Erwartungen richtig zu implementieren erfordert jedoch nicht-triviale Anstrengungen.
Diese Probleme sind nicht einzigartig für eingebaute Klassen. Bei Ihren eigenen Klassen müssen Sie wahrscheinlich die gleichen Entscheidungen treffen. Bei eingebauten Klassen sind jedoch Optimierbarkeit und Sicherheit ein viel größeres Anliegen. Neue eingebaute Methoden konstruieren immer die Basisklasse und rufen so wenige benutzerdefinierte Methoden wie möglich auf. Wenn Sie eingebaute Klassen unterklassen wollen und trotzdem die oben genannten Erwartungen erfüllen möchten, müssen Sie alle Methoden überschreiben, bei denen das Standardverhalten fest integriert ist. Jede Hinzufügung neuer Methoden in die Basisklasse kann auch die Semantik Ihrer Unterklasse brechen, da sie standardmäßig vererbt werden. Daher ist eine bessere Art, eingebaute Klassen zu erweitern, die Zusammensetzung zu verwenden.
extends null wurde entworfen, um die einfache Erstellung von Objekten, die nicht von Object.prototype erben zu ermöglichen. Aufgrund ungelöster Entscheidungen darüber, ob super() im Konstruktor aufgerufen werden sollte, ist es jedoch in der Praxis nicht möglich, eine solche Klasse unter Verwendung einer beliebigen Konstruktorimplementierung zu konstruieren, die kein Objekt zurückgibt. Das TC39 Komitee arbeitet daran, dieses Feature wieder zu aktivieren.
Stattdessen müssen Sie explizit eine Instanz aus dem Konstruktor zurückgeben.
Das erste Beispiel erstellt eine Klasse namens Square von einer Klasse namens Polygon. Dieses Beispiel stammt aus diesem Live-Demo (Quelle).
Klassen können reguläre (nicht konstruierbare) Objekte nicht erweitern. Wenn Sie von einem regulären Objekt erben möchten, indem Sie alle Eigenschaften dieses Objekts in geerbten Instanzen verfügbar machen, können Sie stattdessen Object.setPrototypeOf() verwenden:
Dieses Beispiel erweitert das eingebaute Date Objekt. Dieses Beispiel stammt aus diesem Live-Demo (Quelle).
Alle JavaScript-Objekte erben standardmäßig von Object.prototype, daher scheint das Schreiben von extends Object auf den ersten Blick redundant zu sein. Der einzige Unterschied, extends nicht zu schreiben, besteht darin, dass der Konstruktor selbst statische Methoden von Object erbt, wie zum Beispiel Object.keys(). Da jedoch keine statische Methode von Object den this Wert verwendet, gibt es keinen Nutzen darin, diese statischen Methoden zu erben.
Der Object() Konstruktor behandelt die Unterklassification besonders. Wenn er implizit über super() aufgerufen wird, initialisiert er immer ein neues Objekt mit new.target.prototype als sein Prototyp. Jeder an super() übergebene Wert wird ignoriert.
Vergleichen Sie dieses Verhalten mit einem benutzerdefinierten Wrapper, der die Unterklassification nicht besonders behandelt:
Es kann sein, dass Sie Array Objekte in Ihrer abgeleiteten Array-Klasse MyArray zurückgeben möchten. Das Species-Muster ermöglicht es Ihnen, Standardkonstruktoren zu überschreiben.
Zum Beispiel möchten Sie bei der Verwendung von Methoden wie Array.prototype.map(), die den Standardkonstruktor zurückgeben, dass diese Methoden ein übergeordnetes Array-Objekt zurückgeben, anstelle des MyArray-Objekts. Das Symbol.species Symbol lässt Sie dies tun:
Dieses Verhalten wird von vielen eingebauten Kopiermethoden implementiert. Für Vorbehalte zu diesem Feature siehe die Diskussion zur Unterklassenbildung eingebauter Klassen.
Abstrakte Unterklassen oder Mix-ins sind Vorlagen für Klassen. Eine Klasse kann nur eine einzige Oberklasse haben, daher ist Mehrfachvererbung von Werkzeugklassen beispielsweise nicht möglich. Die Funktionalität muss von der Oberklasse bereitgestellt werden.
Eine Funktion mit einer Oberklasse als Eingabe und einer Unterklasse, die diese Oberklasse erweitert, als Ausgabe kann verwendet werden, um Mix-ins zu implementieren:
Eine Klasse, die diese Mix-ins verwendet, kann dann so geschrieben werden:
Vererbung ist eine sehr starke Kopplungsbeziehung in der objektorientierten Programmierung. Sie bedeutet, dass alle Verhaltensweisen der Basisklasse standardmäßig von der Unterklasse geerbt werden, was nicht immer gewünscht sein könnte. Betrachten Sie zum Beispiel die Implementierung einer ReadOnlyMap:
Es stellt sich heraus, dass ReadOnlyMap nicht konstruierbar ist, da der Map() Konstruktor die set() Methode der Instanz aufruft.
Wir können dies umgehen, indem wir einen privaten Indikator verwenden, um anzuzeigen, ob die Instanz konstruiert wird. Ein wesentliches Problem bei diesem Design ist jedoch, dass es das Liskov'sche Substitutionsprinzip bricht, welches besagt, dass eine Unterklasse für ihre Oberklasse austauschbar sein sollte. Wenn eine Funktion ein Map-Objekt erwartet, sollte sie auch ein ReadOnlyMap-Objekt verwenden können, was hier jedoch zu einem Bruch führt.
Vererbung führt oft zu dem Kreis-Ellipse-Problem, da kein Typ perfekt das Verhalten des anderen beinhaltet, obwohl sie viele gemeinsame Merkmale teilen. Im Allgemeinen ist es, es sei denn, es gibt einen sehr guten Grund, Vererbung zu verwenden, besser, Zusammensetzung einzusetzen. Zusammensetzung bedeutet, dass eine Klasse eine Referenz zu einem Objekt einer anderen Klasse hat und dieses Objekt nur als Implementierungsdetail verwendet.
In diesem Fall ist die ReadOnlyMap Klasse keine Unterklasse von Map, aber sie implementiert dennoch die meisten der gleichen Methoden. Dies bedeutet mehr Code-Duplikation, aber es bedeutet auch, dass die ReadOnlyMap Klasse nicht stark mit der Map Klasse gekoppelt ist und nicht leicht bricht, wenn sich die Map Klasse ändert. Es vermeidet die semantischen Probleme der eingebauten Unterklassifizierung. Zum Beispiel, wenn die Map Klasse eine neue Utility-Methode hinzufügt (wie getOrInsert()), die set() nicht aufruft, würde dies bedeuten, dass die ReadOnlyMap Klasse nicht mehr schreibgeschützt ist, es sei denn, letztere wird entsprechend aktualisiert, um getOrInsert() ebenfalls zu überschreiben. Außerdem haben ReadOnlyMap Objekte die set Methode überhaupt nicht, was genauer ist, als zur Laufzeit einen Fehler auszulösen.
| ECMAScript® 2027 Language Specification # sec-class-definitions |
JavaScript aktivieren, um diese Browser-Kompatibilitätstabelle anzuzeigen.
Der Bauplan für ein besseres Internet.
Besuche die gemeinnützige Muttergesellschaft der Mozilla Corporation, die Mozilla Foundation.
Teile dieses Inhalts sind ©1998–2026 von einzelnen mozilla.org-Mitwirkenden. Inhalte sind verfügbar unter einer Creative-Commons-Lizenz.