Eine Klasse ist der Bauplan für Objekte
Eine JavaScript-Klasse beschreibt, wie ein bestimmter Objekttyp aussieht und was er kann. Du legst den Bauplan einmal an und kannst daraus anschließend beliebig viele Instanzen erzeugen. Jede Instanz hat ihre eigenen Daten, teilt sich aber die gleichen Methoden.
So sieht der grundsätzliche Aufbau aus:
class User { ... } definiert den Bauplan, new User(...) erzeugt daraus eine Instanz. Jede Instanz bekommt ihren eigenen name und ihre eigene email, aber beide teilen sich dieselbe greet-Methode, die einmal an der Klasse hängt.
Zwei Instanzen, zwei Datensätze, ein gemeinsamer Satz an Methoden. Mehr steckt im Grunde nicht dahinter.
Der constructor initialisiert jede Instanz
Der constructor ist eine spezielle Methode, die genau einmal pro Instanz läuft – nämlich in dem Moment, in dem new das Objekt erzeugt. Seine Aufgabe: das frische Objekt aufzusetzen, typischerweise indem er die übergebenen Argumente auf this schreibt:
this zeigt im Konstruktor auf die frisch erzeugte Instanz, die gerade entsteht. Mit this.x = x hängst du x genau an dieses Objekt. Jeder Aufruf von new Point(...) bekommt sein eigenes this – und damit auch sein eigenes x und y.
Wenn du keinen Konstruktor schreibst, spendiert dir JavaScript automatisch einen leeren. Du musst also nur dann selbst einen definieren, wenn es bei der Initialisierung wirklich etwas zu tun gibt.
Warum new der entscheidende Schalter ist
Ein Klassenaufruf ohne new führt direkt zu einem Fehler:
const p = Point(3, 4);
// TypeError: Class constructor Point cannot be invoked without 'new'
Das ist so gewollt. new erledigt vier Dinge, und zwar in dieser Reihenfolge:
- Es legt ein frisches, leeres Objekt an.
- Es verknüpft dieses Objekt mit dem Prototype der Klasse (dort liegen die Methoden).
- Es ruft den
constructorauf, wobeithisan das neue Objekt gebunden wird. - Es gibt das neue Objekt zurück.
Ohne new passiert nichts davon. Die Klasse erzwingt diese Regel, damit du es nicht aus Versehen vergessen kannst – ein echter Fortschritt gegenüber der Zeit vor class, als ein vergessenes new stillschweigend das globale Objekt verunreinigt hat.
Methoden werden geteilt, Felder gehören zur Instanz
Methoden, die du im Klassenrumpf definierst, landen auf dem Prototype – eine einzige Kopie, die sich alle Instanzen teilen. Instanzfelder dagegen (alles, was du an this zuweist) existieren pro Instanz, also jedes Mal als eigene Kopie.
Jede Instanz hat ihr eigenes count – wenn du den einen Zähler hochzählst, bleibt der andere davon unberührt. Aber a.increment und b.increment sind buchstäblich dieselbe Funktion: Sie liegt nur einmal auf dem Prototype und wird bei jedem Aufruf über die Instanz von dort geholt. Genau deshalb skalieren Klassen so günstig – bei tausend Instanzen entstehen eben nicht tausend Kopien jeder Methode.
Instanzfelder (Class Fields)
Instanzfelder kannst du direkt am Anfang des Klassenkörpers deklarieren, also außerhalb des constructor:
Felddeklarationen werden vor dem Constructor-Body ausgeführt – so, als stünden sie ganz am Anfang des constructor. Praktisch sind sie vor allem dann, wenn der Anfangswert nicht von Constructor-Argumenten abhängt. Das bleibt übersichtlicher, als den Constructor mit this.count = 0; this.step = 1; vollzupacken.
Hängen die Werte dagegen doch von Argumenten ab, gehört die Zuweisung in den Constructor – dort sind die Argumente schließlich im Scope.
Getter und Setter in JavaScript
Ein Getter oder Setter sieht aus wie eine Methode, verhält sich aber wie ein Property-Zugriff. Du liest oder schreibst den Wert ohne Klammern, und im Hintergrund läuft die Methode:
Beachte, wie das Ganze aufgerufen wird: t.fahrenheit (ohne Klammern) liest den Getter aus, t.fahrenheit = 100 löst den Setter aus. Von außen betrachtet sieht fahrenheit aus wie eine ganz normale Eigenschaft – tatsächlich wird der Wert aber bei jedem Zugriff neu aus celsius berechnet.
Getter sind ideal für abgeleitete Werte. Setter wiederum eignen sich gut, um einen Wert bei der Zuweisung zu validieren oder zu normalisieren. Übertreib es aber nicht damit: Wenn ein Getter aufwendig ist, wundern sich Leser deines Codes später, warum ein simpler "Property-Zugriff" plötzlich ordentlich Arbeit verrichtet.
Method Shorthand, Computed Names und this
Methoden in einer JavaScript-Klasse nutzen die Kurzschreibweise – kein function-Keyword, kein : zwischen Name und Rumpf:
Wenn add am Ende this zurückgibt, lässt sich Method Chaining umsetzen – jeder Aufruf gibt dieselbe Instanz zurück, sodass die nächste Methode direkt darauf weiterarbeiten kann.
Ein Stolperstein dabei: Methoden sind nicht automatisch an ihre Instanz gebunden. Sobald du eine Methode aus dem Objekt herauslöst und einzeln aufrufst, ist this weg:
Der Aufruf g.hello() funktioniert, weil der Punkt this automatisch mitliefert. Ein freistehender Aufruf fn() dagegen nicht. Wenn du eine losgelöste, vorgebundene Methode brauchst (typischer Fall bei Event-Handlern), binde sie entweder im Constructor oder nutze ein Arrow-Function-Klassenfeld: hello = () => \Hi, ${this.name}`;`.
Ein kleines lauffähiges Beispiel
Jetzt setzen wir die Bausteine zusammen — Instanzfelder, Constructor, Methoden und ein Getter:
Auf den ersten Blick verständlich: Die Klasse beschreibt genau, was ein BankAccount ist und was er können soll.
JavaScript Klassen sind eigentlich Funktionen
Eins solltest du dir merken: class ist im Grunde nur syntaktischer Zucker. Unter der Haube ist eine JavaScript Klasse nichts anderes als eine Funktion – genauer gesagt eine Constructor-Funktion – und ihre Methoden hängen an ClassName.prototype:
typeof User ergibt "function". greet hängt an User.prototype. Instanzen finden greet über die Prototype Chain – also genau den Mechanismus, den die Sprache seit ihren Anfängen kennt. Klassen liefern dir einfach eine saubere Syntax, um das Ganze aufzusetzen.
Dieses Mental Model zahlt sich später aus: Vererbung, instanceof und das Debuggen von Prototype Chains ergeben viel mehr Sinn, sobald du im Kopf hast, dass eine Klasse nichts anderes ist als eine Funktion mit Methoden an ihrem Prototype.
Als Nächstes: Vererbung
Bisher steht jede Klasse für sich allein. In realen Projekten arbeitest du aber meist mit Klassenhierarchien – ein Dog ist eine Art Animal, ein AdminUser eine Art User. Genau dafür gibt es die Keywords extends und super, und die schauen wir uns als Nächstes an.
Häufig gestellte Fragen
Wie legt man in JavaScript eine Klasse an?
Du schreibst das Keyword class, gefolgt vom Namen, und packst in den Block eine constructor-Methode sowie beliebige weitere Methoden. Eine Instanz erzeugst du dann mit new ClassName(...). Beispiel: class User { constructor(name) { this.name = name; } } — und anschließend new User('Ada').
Wofür ist der Konstruktor in einer JavaScript-Klasse da?
Der constructor läuft genau einmal, nämlich beim Aufruf von new ClassName(...). Seine Aufgabe ist es, die neue Instanz zu initialisieren — typischerweise werden die Argumente als Eigenschaften auf this gesetzt. Schreibst du keinen, bekommst du automatisch einen leeren Default-Konstruktor.
Was ist der Unterschied zwischen einer Klasse und einer Funktion in JavaScript?
Unter der Haube ist eine Klasse nichts anderes als eine Funktion — genauer gesagt eine Konstruktorfunktion, deren Methoden am Prototype hängen. Das class-Keyword ist also größtenteils syntaktischer Zucker, erzwingt aber den Aufruf mit new, macht extends sauber nutzbar und sorgt dafür, dass Methoden nicht enumerierbar sind. Für neuen Code solltest du daher immer class statt handgeschriebener Konstruktorfunktionen verwenden.