Start · Sprachen · JavaScript · Referenz · Öffentliche Klassenfelder

Öffentliche Klassenfelder

Keyword

Öffentliche Klassenfelder legen beschreibbare, aufzählbare und konfigurierbare Eigenschaften direkt auf jeder Instanz oder (mit <code>static</code>) auf dem Konstruktor selbst an.

seit JavaScript ES2022 Kategorie: syntax

Signatur

class Klasse { feldName = initialwert; static statischesFeld = initialwert; }

Beschreibung

Mit öffentlichen Klassenfeldern deklarieren Sie Eigenschaften direkt im Klassenrumpf, statt sie im constructor per this.x = … zuzuweisen. Jedes Instanzfeld wird für jede neue Instanz als eigene Eigenschaft angelegt, also nicht auf dem Prototyp. Die Semantik entspricht Object.defineProperty() mit writable, enumerable und configurable jeweils auf true.

Statische Felder (static name = wert) werden einmalig auf dem Klassenkonstruktor selbst definiert, zum Beispiel für Konstanten, Zähler oder Caches. Der Initialisierer wird beim Auswerten der Klassendefinition ausgeführt, und this verweist dort auf die Klasse.

Initialisierer von Instanzfeldern werden bei jeder Instanziierung ausgewertet, und zwar in der Reihenfolge ihrer Deklaration. In Basisklassen passiert das direkt zu Beginn des Konstruktors, in abgeleiteten Klassen unmittelbar nachdem super() zurückgekehrt ist. Innerhalb des Initialisierers verweist this auf die entstehende Instanz. Deshalb eignen sich Felder mit Pfeilfunktionen gut für Event-Handler, deren this fest an die Instanz gebunden sein soll.

Feldnamen können Bezeichner, Zeichenketten, Zahlen oder berechnete Schlüssel ([ausdruck]) sein. Ein Feld ohne Initialisierer erhält den Wert undefined. Für wirklich gekapselte Daten gibt es private Felder mit dem Präfix #.

Parameter

Name Typ Default Beschreibung
$static Schlüsselwort Optional. Definiert das Feld auf dem Klassenkonstruktor statt auf jeder Instanz.
$feldName Pflicht Bezeichner | string | number | [berechneter Schlüssel] Name der Eigenschaft. Berechnete Schlüssel wie [Symbol.iterator] werden nur einmal ausgewertet, und zwar beim Auswerten der Klassendefinition.
$initialwert any undefined Ausdruck, der bei jeder Instanziierung (bzw. einmalig bei static) ausgewertet wird. this verweist auf die Instanz bzw. auf die Klasse.

Rückgabewert

Typ
void
Beschreibung
Eine Deklaration liefert keinen Wert. Sie erzeugt eine eigene Eigenschaft auf der Instanz bzw. auf dem Konstruktor.

Beispiele

Instanzfelder statt Konstruktor-Zuweisungen

class Warenkorb {
  artikel = [];
  rabatt = 0;
  waehrung = 'EUR';

  hinzufuegen(name, preis) {
    this.artikel.push({ name, preis });
    return this;
  }

  get summe() {
    const brutto = this.artikel.reduce((s, a) => s + a.preis, 0);
    return brutto * (1 - this.rabatt);
  }
}

const korb = new Warenkorb().hinzufuegen('Buch', 20).hinzufuegen('Stift', 5);
korb.rabatt = 0.1;
console.log(korb.summe, korb.waehrung);
console.log(Object.keys(korb));
22.5 EUR [ 'artikel', 'rabatt', 'waehrung' ]

Statische Felder für Konstanten und Zähler

class Benutzer {
  static anzahl = 0;
  static ROLLEN = Object.freeze(['gast', 'nutzer', 'admin']);

  id = ++Benutzer.anzahl;
  rolle = Benutzer.ROLLEN[0];

  constructor(name) {
    this.name = name;
  }
}

const a = new Benutzer('Anna');
const b = new Benutzer('Ben');
console.log(a.id, b.id, Benutzer.anzahl);
console.log(b.rolle, 'anzahl' in b);
1 2 2 gast false

Pfeilfunktion als gebundener Event-Handler

class Zaehler {
  wert = 0;

  // this ist dauerhaft an die Instanz gebunden
  erhoehen = () => {
    this.wert++;
    console.log(`Wert: ${this.wert}`);
  };
}

const z = new Zaehler();
const handler = z.erhoehen; // Methode vom Objekt gelöst
handler();
setTimeout(z.erhoehen, 0);
// Im Browser: button.addEventListener('click', z.erhoehen);
Wert: 1 Wert: 2

Initialisierungsreihenfolge bei Vererbung

class Basis {
  typ = 'basis';
  constructor() {
    console.log('Basis-Konstruktor sieht:', this.typ);
  }
}

class Kind extends Basis {
  typ = 'kind';
  constructor() {
    super(); // Kind-Felder existieren erst nach super()
    console.log('Kind-Konstruktor sieht:', this.typ);
  }
}

new Kind();
Basis-Konstruktor sieht: basis Kind-Konstruktor sieht: kind

// Wichtig · Fallstricke

  • Definieren statt Zuweisen: Felder werden per [[DefineOwnProperty]] angelegt, nicht per Zuweisung. Setter der Oberklasse werden daher nicht aufgerufen. Stattdessen überdeckt das Feld den Accessor mit einer eigenen Dateneigenschaft. Das weicht von this.x = … im Konstruktor ab und auch von älteren Babel- oder TypeScript-Einstellungen (useDefineForClassFields: false).
  • Reihenfolge bei Vererbung: Die Felder einer Unterklasse gibt es erst nach super(). Ruft der Konstruktor der Basisklasse eine überschriebene Methode auf, die auf solche Felder zugreift, liest diese undefined.
  • Speicherbedarf: Pfeilfunktionen in Feldern werden für jede Instanz neu erzeugt und liegen nicht auf dem Prototyp. Bei sehr vielen Instanzen kostet das mehr Speicher, außerdem lassen sie sich nicht per super.methode() aufrufen.
  • Veränderliche Standardwerte wie artikel = [] sind unbedenklich. Der Initialisierer läuft für jede Instanz neu, die Instanzen teilen sich also kein Array.
  • Felder, deren Name constructor lautet, sind nicht erlaubt. Auch statische Felder mit den Namen prototype oder constructor sind verboten.
  • In Initialisierern ist arguments nicht verfügbar (SyntaxError).
  • Kompatibilität: Unterstützt in Chrome 72+, Firefox 69+ (statische Felder ab 75), Safari 14+ (statische Felder ab 14.1) und Node.js 12+. Für ältere Umgebungen ist eine Transpilierung nötig.