Öffentliche Klassenfelder
Baseline
Weitgehend verfügbar
Diese Funktion ist gut etabliert und funktioniert auf vielen Geräten und in vielen Browserversionen. Sie ist seit September 2022 browserübergreifend verfügbar.
Öffentliche Felder sind beschreibbare, aufzählbare und konfigurierbare Eigenschaften, die auf jeder Klasseninstanz oder auf dem Klassenkonstruktor definiert sind.
Syntax
class ClassWithField {
instanceField;
instanceFieldWithInitializer = "instance field";
static staticField;
static staticFieldWithInitializer = "static field";
}
Es gelten einige zusätzliche Syntaxbeschränkungen:
- Der Name einer statischen Eigenschaft (Feld oder Methode) darf nicht
prototypesein. - Der Name eines Klassenfelds (statisch oder instanzbezogen) darf nicht
constructorsein.
Beschreibung
Diese Seite erläutert öffentliche Instanzfelder im Detail.
- Informationen zu öffentlichen statischen Feldern finden Sie unter
static. - Informationen zu privaten Feldern finden Sie unter private Elemente.
- Informationen zu öffentlichen Methoden finden Sie unter Methodendefinitionen.
- Informationen zu öffentlichen Accessors finden Sie unter getter und setter.
Öffentliche Instanzfelder sind auf jeder erstellten Instanz einer Klasse vorhanden. Indem Sie ein öffentliches Feld deklarieren, stellen Sie sicher, dass das Feld immer vorhanden ist. Zugleich wird die Klassendefinition dadurch aussagekräftiger.
Öffentliche Instanzfelder werden der Instanz entweder beim Erstellen in der Basisklasse hinzugefügt (bevor der Konstruktorcode ausgeführt wird) oder in einer abgeleiteten Klasse unmittelbar nach der Rückkehr von super(). Felder ohne Initialisierer werden mit undefined initialisiert. Wie bei Eigenschaften können Feldnamen berechnet werden.
const PREFIX = "prefix";
class ClassWithField {
field;
fieldWithInitializer = "instance field";
[`${PREFIX}Field`] = "prefixed field";
}
const instance = new ClassWithField();
console.log(Object.hasOwn(instance, "field")); // true
console.log(instance.field); // undefined
console.log(instance.fieldWithInitializer); // "instance field"
console.log(instance.prefixField); // "prefixed field"
Berechnete Feldnamen werden nur einmal ausgewertet, und zwar zum Zeitpunkt der Klassendefinition. Das bedeutet, dass jede Klasse immer einen festen Satz von Feldnamen hat und zwei Instanzen nicht durch berechnete Namen unterschiedliche Feldnamen erhalten können. Der this-Wert im berechneten Ausdruck ist der this-Wert der Umgebung der Klassendefinition. Ein Verweis auf den Namen der Klasse führt zu einem ReferenceError, weil die Klasse zu diesem Zeitpunkt noch nicht initialisiert ist. await und yield funktionieren in diesem Ausdruck wie erwartet.
class C {
[Math.random()] = 1;
}
console.log(new C());
console.log(new C());
// Both instances have the same field name
Im Feldinitialisierer verweist this auf die gerade erstellte Klasseninstanz und super auf die prototype-Eigenschaft der Basisklasse. Diese enthält die Instanzmethoden der Basisklasse, nicht aber ihre Instanzfelder.
class Base {
baseField = "base field";
anotherBaseField = this.baseField;
baseMethod() {
return "base method output";
}
}
class Derived extends Base {
subField = super.baseMethod();
}
const base = new Base();
const sub = new Derived();
console.log(base.anotherBaseField); // "base field"
console.log(sub.subField); // "base method output"
Der Ausdruck des Feldinitialisierers wird jedes Mal ausgewertet, wenn eine neue Instanz erstellt wird. (Da sich der this-Wert für jede Instanz unterscheidet, kann der Initialisierungsausdruck auf instanzspezifische Eigenschaften zugreifen.)
class C {
obj = {};
}
const instance1 = new C();
const instance2 = new C();
console.log(instance1.obj === instance2.obj); // false
Der Ausdruck wird synchron ausgewertet. Sie können await oder yield nicht im Initialisierungsausdruck verwenden. (Stellen Sie sich den Initialisierungsausdruck so vor, als wäre er implizit in eine Funktion eingebettet.)
Da Instanzfelder einer Klasse hinzugefügt werden, bevor der jeweilige Konstruktor ausgeführt wird, können Sie innerhalb des Konstruktors auf ihre Werte zugreifen. Da Instanzfelder einer abgeleiteten Klasse jedoch erst definiert werden, nachdem super() zurückgekehrt ist, hat der Konstruktor der Basisklasse keinen Zugriff auf die Felder der abgeleiteten Klasse.
class Base {
constructor() {
console.log("Base constructor:", this.field);
}
}
class Derived extends Base {
field = 1;
constructor() {
super();
console.log("Derived constructor:", this.field);
this.field = 2;
}
}
const instance = new Derived();
// Base constructor: undefined
// Derived constructor: 1
console.log(instance.field); // 2
Felder werden einzeln hinzugefügt. Feldinitialisierer können auf die Werte darüber deklarierter Felder zugreifen, nicht aber auf die Werte darunter deklarierter Felder. Alle Instanzmethoden und statischen Methoden werden zuvor hinzugefügt und können aufgerufen werden. Wenn sie auf Felder verweisen, die unterhalb des gerade initialisierten Felds deklariert sind, verhalten sie sich dabei möglicherweise nicht wie erwartet.
class C {
a = 1;
b = this.c;
c = this.a + 1;
d = this.c + 1;
}
const instance = new C();
console.log(instance.d); // 3
console.log(instance.b); // undefined
Hinweis:
Bei privaten Feldern ist dies besonders wichtig: Der Zugriff auf ein noch nicht initialisiertes privates Feld löst einen TypeError aus, selbst wenn das private Feld weiter unten deklariert ist. (Ist das private Feld überhaupt nicht deklariert, würde bereits frühzeitig ein SyntaxError auftreten.)
Da Klassenfelder mit der Semantik von [[DefineOwnProperty]] hinzugefügt werden (was im Wesentlichen Object.defineProperty() entspricht), rufen Felddeklarationen in abgeleiteten Klassen keine Setter der Basisklasse auf. Dieses Verhalten unterscheidet sich von der Verwendung von this.field = … im Konstruktor.
class Base {
set field(val) {
console.log(val);
}
}
class DerivedWithField extends Base {
field = 1;
}
const instance = new DerivedWithField(); // No log
class DerivedWithConstructor extends Base {
constructor() {
super();
this.field = 1;
}
}
const instance2 = new DerivedWithConstructor(); // Logs 1
Hinweis:
Bevor die Spezifikation für Klassenfelder mit der [[DefineOwnProperty]]-Semantik fertiggestellt wurde, wandelten die meisten Transpiler, darunter Babel und tsc, Klassenfelder in die Form DerivedWithConstructor um. Nach der Standardisierung von Klassenfeldern hat dies zu schwer erkennbaren Fehlern geführt.
Beispiele
>Klassenfelder verwenden
Klassenfelder können nicht von den Argumenten des Konstruktors abhängen. Daher ergeben Feldinitialisierer in der Regel für jede Instanz denselben Wert (es sei denn, derselbe Ausdruck kann bei jeder Auswertung einen anderen Wert ergeben, etwa Math.random() oder Objektinitialisierer).
class Person {
name = nameArg; // nameArg is out of scope of the constructor
constructor(nameArg) {}
}
class Person {
// All instances of Person will have the same name
name = "Dragomir";
}
Selbst die Deklaration eines leeren Klassenfelds ist jedoch nützlich: Sie zeigt an, dass das Feld existiert. So können sowohl Typprüfprogramme als auch menschliche Leser die Struktur der Klasse statisch analysieren.
class Person {
name;
age;
constructor(name, age) {
this.name = name;
this.age = age;
}
}
Der obige Code wirkt repetitiv. Wenn this jedoch dynamisch verändert wird, macht die explizite Felddeklaration deutlich, welche Felder auf der Instanz auf jeden Fall vorhanden sein werden.
class Person {
name;
age;
constructor(properties) {
Object.assign(this, properties);
}
}
Da Initialisierer erst ausgewertet werden, nachdem die Basisklasse ausgeführt wurde, können Sie auf Eigenschaften zugreifen, die der Konstruktor der Basisklasse erstellt hat.
class Person {
name;
age;
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class Professor extends Person {
name = `Professor ${this.name}`;
}
console.log(new Professor("Radev", 54).name); // "Professor Radev"
Spezifikationen
| Spezifikation |
|---|
| ECMAScript® 2027 Language Specification> # prod-FieldDefinition> |
Browser-Kompatibilität
Siehe auch
- Leitfaden Klassen verwenden
- Klassen
- Private Elemente
class- Die Semantik aller JS-Klassenelemente von Shu-yu Guo (2018)
- Öffentliche und private Klassenfelder auf v8.dev (2018)