Klassen
Baseline
Weitgehend verfügbar
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.
Klassen sind eine Vorlage zur Erstellung von Objekten. Sie kapseln Daten mit Code, um mit diesen Daten zu arbeiten. Klassen in JS basieren auf Prototypen, haben jedoch auch einige Syntax- und Semantikmerkmale, die einzigartig für Klassen sind.
Für mehr Beispiele und Erklärungen siehe den Leitfaden zur Verwendung von Klassen.
Beschreibung
>Definieren von Klassen
Klassen sind tatsächlich "besondere Funktionen", und genauso wie Sie Funktionsausdrücke und Funktionsdeklarationen definieren können, kann eine Klasse auf zwei Arten definiert werden: ein Klassen-Ausdruck oder eine Klassen-Deklaration.
// Declaration
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// Expression; the class is anonymous but assigned to a variable
const Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// Expression; the class has its own name
const Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Wie Funktionsausdrücke können Klassen-Ausdrücke anonym sein oder einen Namen haben, der von der Variablen, der sie zugewiesen sind, abweicht. Anders als Funktionsdeklarationen haben Klassendeklarationen jedoch die gleichen Einschränkungen der temporal dead zone wie let oder const und verhalten sich, als ob sie nicht gehoben würden.
Klassenkörper
Der Körper einer Klasse ist der Teil, der in geschweiften Klammern {} steht. Hier definieren Sie Klassenmitglieder, wie Methoden oder Konstruktor.
Der Körper einer Klasse wird im strict mode ausgeführt, auch ohne die "use strict"-Direktive.
Ein Klassenelement kann durch drei Aspekte charakterisiert werden:
- Art: Getter, Setter, Methode oder Feld
- Ort: Statisch oder Instanz
- Sichtbarkeit: Öffentlich oder privat
Zusammen ergeben sie 16 mögliche Kombinationen. Um die Referenz logischer zu unterteilen und überlappenden Inhalt zu vermeiden, werden die verschiedenen Elemente detailliert auf verschiedenen Seiten vorgestellt:
- Methodendefinitionen
-
Öffentliche Instanzmethode
- getter
-
Öffentlicher Instanzgetter
- setter
-
Öffentlicher Instanzsetter
- Öffentliche Klassenfelder
-
Öffentliches Instanzfeld
static-
Öffentliche statische Methode, Getter, Setter und Feld
- Private Elemente
-
Alles, was privat ist
Hinweis: Private Elemente unterliegen der Einschränkung, dass alle in derselben Klasse deklarierten privaten Namen eindeutig sein müssen. Alle anderen öffentlichen Eigenschaften unterliegen dieser Einschränkung nicht — Sie können mehrere öffentliche Eigenschaften mit demselben Namen haben, wobei die letzte die anderen überschreibt. Dies ist dasselbe Verhalten wie bei Objektinitialisierern.
Zusätzlich gibt es zwei spezielle Klassenelement-Syntaxen: constructor und statische Initialisierungsblöcke, mit ihren eigenen Referenzen.
Konstruktor
Die constructor-Methode ist eine spezielle Methode zum Erstellen und Initialisieren eines mit einer Klasse erstellten Objekts. Es kann nur eine spezielle Methode mit dem Namen "constructor" in einer Klasse geben — es wird ein SyntaxError geworfen, wenn die Klasse mehr als eine constructor-Methode enthält.
Ein Konstruktor kann das super-Schlüsselwort verwenden, um den Konstruktor der Superklasse aufzurufen.
Sie können Instanzeigenschaften innerhalb des Konstruktors erstellen:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Alternativ, wenn die Werte Ihrer Instanzeigenschaften nicht von den Argumenten des Konstruktors abhängen, können Sie sie als Klassenfelder definieren.
Statische Initialisierungsblöcke
Statische Initialisierungsblöcke ermöglichen eine flexible Initialisierung von statischen Eigenschaften, einschließlich der Auswertung von Anweisungen während der Initialisierung, während der Zugriff auf den privaten Bereich gewährt wird.
Mehrere statische Blöcke können deklariert werden und diese können mit der Deklaration von statischen Feldern und Methoden verknüpft werden (alle statischen Elemente werden in der Deklarationsreihenfolge ausgewertet).
Methoden
Methoden werden auf dem Prototyp jeder Klasseninstanz definiert und allen Instanzen gemeinsam genutzt. Methoden können einfache Funktionen, asynchrone Funktionen, Generatorfunktionen oder asynchrone Generatorfunktionen sein. Für weitere Informationen siehe Methodendefinitionen.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
*getSides() {
yield this.height;
yield this.width;
yield this.height;
yield this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
console.log([...square.getSides()]); // [10, 10, 10, 10]
Statische Methoden und Felder
Das static-Schlüsselwort definiert eine statische Methode oder ein Feld für eine Klasse. Statische Eigenschaften (Felder und Methoden) sind an der Klasse selbst statt an jeder Instanz definiert. Statische Methoden werden häufig verwendet, um Utility-Funktionen für eine Anwendung zu erstellen, während statische Felder nützlich für Caches, feste Konfigurationen oder andere Daten sind, die nicht über Instanzen hinweg dupliziert werden müssen.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined
console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755
Felderklärung
Mit der Klassenfelddeklarationssyntax kann das Konstruktor-Beispiel wie folgt geschrieben werden:
class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Klassenfelder ähneln Objekt-Eigenschaften, nicht Variablen, daher verwenden wir keine Schlüsselwörter wie const, um sie zu deklarieren. In JavaScript verwenden private Elemente eine spezielle Bezeichnersyntax, daher sollten Modifikatorschlüsselwörter wie public und private ebenfalls nicht verwendet werden.
Wie oben gezeigt, können die Felder mit oder ohne Standardwert deklariert werden. Felder ohne Standardwerte haben standardmäßig undefined. Durch die frühzeitige Deklaration der Felder werden Klassendefinitionen selbstdokumentierender, und die Felder sind immer vorhanden, was Optimierungen erleichtert.
Siehe öffentliche Klassenfelder für weitere Informationen.
Private Elemente
Mit privaten Feldern kann die Definition wie folgt verfeinert werden.
class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}
Es ist ein Fehler, von außerhalb der Klasse auf private Felder zu verweisen; sie können nur innerhalb des Klassenkörpers gelesen oder geschrieben werden. Indem Sie Dinge definieren, die außerhalb der Klasse nicht sichtbar sind, stellen Sie sicher, dass die Benutzer Ihrer Klassen nicht von internen Details abhängen können, die sich von Version zu Version ändern können.
Private Felder können nur im Voraus in einer Felderklärung deklariert werden. Sie können nicht später durch Zuweisung zu ihnen erstellt werden, wie es bei normalen Eigenschaften der Fall ist.
Private Methoden und Zugriffsmethoden können ebenfalls unter Verwendung derselben Syntax wie ihre öffentlichen Gegenstücke definiert werden, jedoch beginnt der Bezeichner mit #.
Für weitere Informationen siehe private Elemente.
Vererbung
Das extends-Schlüsselwort wird in Klassendeklarationen oder Klassenausdrücken verwendet, um eine Klasse als Kind eines anderen Konstruktors (entweder eine Klasse oder eine Funktion) zu erstellen.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(`${this.name} barks.`);
}
}
const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.
Wenn im Unterklasse ein Konstruktor vorhanden ist, muss er zuerst super() aufrufen, bevor er this verwendet. Das super-Schlüsselwort kann auch verwendet werden, um entsprechende Methoden der Superklasse aufzurufen.
class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(`${this.name} roars.`);
}
}
const l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.
Ausführungsreihenfolge
Wenn eine class Deklaration oder ein class Ausdruck ausgewertet wird, werden seine verschiedenen Komponenten in der folgenden Reihenfolge ausgewertet:
- Die
extends-Klausel, falls vorhanden, wird zuerst ausgewertet. Sie muss zu einer gültigen Konstruktorfunktion odernullausgewertet werden, andernfalls wird einTypeErrorgeworfen. - Die
constructor-Methode wird extrahiert, mit einer Standardimplementierung ersetzt, wennconstructornicht vorhanden ist. Da dieconstructor-Definition jedoch nur eine Methodendefinition ist, ist dieser Schritt nicht beobachtbar. - Die Eigenschaftsschlüssel der Klassenelemente werden in der Reihenfolge der Deklaration ausgewertet. Wenn der Eigenschaftsschlüssel berechnet wird, wird der berechnete Ausdruck ausgewertet, wobei der Wert
thisauf den umgebenden Wertthisder Klasse gesetzt wird (nicht die Klasse selbst). Keine der Eigenschaftswerte wird bisher ausgewertet. - Methoden und Zugriffsmethoden werden in der Reihenfolge der Deklaration installiert. Instanzmethoden und Zugriffsmethoden werden auf der
prototypeEigenschaft der aktuellen Klasse installiert, und statische Methoden und Zugriffsmethoden werden auf der Klasse selbst installiert. Private Instanzmethoden und Zugriffsmethoden werden gespeichert, um später direkt auf der Instanz installiert zu werden. Dieser Schritt ist nicht beobachtbar. - Die Klasse ist nun mit dem durch
extendsangegebenen Prototyp und der durchconstructorangegebenen Implementierung initialisiert. Bei allen obigen Schritten, wenn ein ausgewerteter Ausdruck versucht, auf den Namen der Klasse zuzugreifen, wird einReferenceErrorgeworfen, da die Klasse noch nicht initialisiert ist. - Die Werte der Klassenelemente werden in der Reihenfolge der Deklaration ausgewertet:
- Für jedes Instanzfeld (öffentlich oder privat), wird sein Initialisierer-Ausdruck gespeichert. Der Initialisierer wird während der Instanzerstellung ausgewertet, am Anfang des Konstruktors (für Basisklassen) oder unmittelbar bevor der
super()-Aufruf zurückkehrt (für abgeleitete Klassen). - Für jedes statische Feld (öffentlich oder privat), wird sein Initialisierer mit
thisauf die Klasse selbst gesetzt ausgewertet und die Eigenschaft wird auf der Klasse erstellt. - Statische Initialisierungsblöcke werden mit
thisauf die Klasse selbst gesetzt ausgewertet.
- Für jedes Instanzfeld (öffentlich oder privat), wird sein Initialisierer-Ausdruck gespeichert. Der Initialisierer wird während der Instanzerstellung ausgewertet, am Anfang des Konstruktors (für Basisklassen) oder unmittelbar bevor der
- Die Klasse ist nun vollständig initialisiert und kann als Konstruktionsfunktion verwendet werden.
Für die Erstellung von Instanzen siehe die constructor-Referenz.
Beispiele
>Binden von this mit Instanz- und statischen Methoden
Wenn eine statische oder Instanzmethode ohne Wert für this aufgerufen wird, z. B. durch Zuweisung der Methode zu einer Variablen und anschließendem Aufruf, ist der this-Wert innerhalb der Methode undefined. Dieses Verhalten ist dasselbe, auch wenn die "use strict"-Direktive nicht vorhanden ist, da der Code innerhalb des class-Körpers immer im strict mode ausgeführt wird.
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
const obj = new Animal();
obj.speak(); // the Animal object
const speak = obj.speak;
speak(); // undefined
Animal.eat(); // class Animal
const eat = Animal.eat;
eat(); // undefined
Wenn wir das oben Genannte unter Verwendung traditioneller funktionsbasierter Syntax im nicht-strict mode umschreiben, werden this-Methodenaufrufe automatisch an globalThis gebunden. Im strict mode bleibt der Wert von this als undefined.
function Animal() {}
Animal.prototype.speak = function () {
return this;
};
Animal.eat = function () {
return this;
};
const obj = new Animal();
const speak = obj.speak;
speak(); // global object (in non–strict mode)
const eat = Animal.eat;
eat(); // global object (in non-strict mode)
Spezifikationen
| Spezifikation |
|---|
| ECMAScript® 2027 Language Specification> # sec-class-definitions> |
Browser-Kompatibilität
Siehe auch
- Leitfaden zur Verwendung von Klassen
classclassAusdruck- Funktionen
- ES6 In Depth: Klassen auf hacks.mozilla.org (2015)