Seit ES2015 definiert man Objekte mit gemeinsamem Verhalten meist über Klassen. Intern arbeitet JavaScript weiterhin mit Prototypen. class ist eine klarere und strengere Schreibweise für Konstruktorfunktionen.
Grundaufbau
class User {
role = 'gast'; // öffentliches Feld
#passwort; // privates Feld
constructor(name, passwort) {
this.name = name;
this.#passwort = passwort;
}
get anzeigeName() {
return `${this.name} (${this.role})`;
}
pruefe(eingabe) {
return eingabe === this.#passwort;
}
static gast() {
return new User('Gast', '');
}
}
const u = new User('Ada', 'geheim');
u.anzeigeName; // 'Ada (gast)'
u.pruefe('geheim'); // true
Methoden liegen auf User.prototype, Felder auf jeder Instanz. Auf #-Felder kann man von außen tatsächlich nicht zugreifen. Ein Versuch führt schon beim Parsen zu einem SyntaxError.
Vererbung mit extends und super
class Admin extends User {
role = 'admin';
constructor(name, passwort, rechte = []) {
super(name, passwort); // vor jedem this-Zugriff
this.rechte = rechte;
}
get anzeigeName() {
return `★ ${super.anzeigeName}`;
}
}
new Admin('Linus', 'root') instanceof User; // true
In abgeleiteten Klassen muss super() aufgerufen werden, bevor this verwendet wird. Sonst gibt es einen ReferenceError. Auch eingebaute Typen wie Error oder Array lassen sich erweitern.
this in Callbacks
Methoden sind nicht automatisch an ihr Objekt gebunden. Übergibt man eine Methode als Callback, geht this verloren. Abhilfe schafft eine Arrow-Function als Klassenfeld:
class Zaehler {
wert = 0;
erhoehe = () => { this.wert++; };
}
const z = new Zaehler();
button.addEventListener('click', z.erhoehe);
Der Nachteil: Arrow-Felder werden für jede Instanz neu erzeugt und liegen nicht auf dem Prototyp.
Fallstricke
- Klassen werden nicht wie Funktionen gehoistet. Wer sie vor der Deklaration nutzt, bekommt einen
ReferenceError(TDZ). - Der Klassenkörper läuft immer im Strict Mode.
- Ein Aufruf ohne
newwirft einenTypeError. - Private Member und
static-Blöcke gibt es seit ES2022. Alle aktuellen Browser unterstützen sie.