Eigenschaften & Methoden
Klassen bestehen aus Eigenschaften (Daten) und Methoden (Operationen). Mithilfe von Zugriffsmodifizierern kann Kapselung erreicht werden: der Zugriff auf die Eigenschaften wird eingeschränkt und über Methoden kontrolliert. Diese Einschränkungen sind entscheidend um Abstraktionen entwickeln zu können.
Eigenschaften (Properties)
Eine Eigenschaft wird mit einem Namen und einem Datentyp deklariert. Jede Eigenschaft sollte initialisiert werden. Optionale Eigenschaften werden mit dem Suffix ? deklariert und müssen nicht initialisiert werden. Dank Typinferenz muss der Datentyp nicht unbedingt angegeben werden, wenn ein Initialwert definiert ist. Für eine bessere Lesbarkeit empfiehlt es sich aber, jede Eigenschaft mit einer Typannotation zu versehen. Hierzu ein Beispiel:
Info
Der Konstruktor der Klasse Date erwartet den Monat mit einem 0-basierenden Index (0: Januar, 1: Februar, …, 11: Dezember). new Date(2025, 11, 30) bezeichnet also den 30. Dezember 2025, nicht den 30. November 2025.
Die Klasse Person verfügt über einen Vornamen (firstName), einen optionalen Zweitnamen (middleName) und über einen Nachnamen (lastName). Weiter verfügt die Klasse über ein Geburts- (born) sowie ein optionales Todesdatum (died). Die Klasse kann folgendermassen verwendet werden:
Würde man Person als reinen Objekttyp definieren, sähe der Code folgendermassen aus:
Dieser Code sieht wesentlich kompakter aus. Als reine Datenbehälter sind Objekttypen also offenbar besser geeignet als Klassen.
Methoden (Methods)
Klassen sind erst dann gegenüber Objekttypen von Vorteil, wenn Methoden auf den die Daten eines Objekts zugreifen sollen. Eine Methode ist eine Funktion, welche auf der Instanz einer Klasse operiert und mit dem this-Schlüsselwort Zugriff auf das konkrete Objekt (die Instanz) erhält:
Die Methode show gibt einen String zurück, der den vollen Namen und die Lebensdaten der jeweiligen Person enthält. Die Hilfsmethode formatYMD formatiert ein gegebenes Datum nach dem Format YYYY-MM-DD.
Diese Klasse kann nun folgendermassen verwendet werden:
Dadurch wurde eine gewisse Kapselung erreicht: Die show()-Methode kümmert sich um die Details der Formatierung. Je nachdem, welche Daten definiert werden (optionaler Zweitname und optionales Todesdatum), sieht das Ergebnis dann anders aus:
Konstruktoren (Constructors)
Objektliteralen haben den Vorteil, dass man die initialen Daten bei der Objekterstellung mitgeben kann, ohne mehrere Zuweisungesoperationen durchführen zu müssen.
Mithilfe von einem Konstruktor können die initialen Daten dem Objekt bei seiner Erstellung mitgegeben werden:
Eigenschaften, die nicht zwingend benötigt werden, können über optionale Konstruktorparameter mitgegeben werden. Optionale Parameter sind mit dem Suffix ? bezeichnet stehen am Ende der Parameterliste.
Instanzen der Klasse Person lassen sich nun folgendermassen initialisieren:
Eine bereits initialisierte Eigenschaft wird durch die Zuweisung im Konstruktor überschrieben.
Unveränderbarkeit (Immutability)
Es gibt Eigenschaften, die sich nach der Initialisierung nicht mehr ändern können. Solche unveränderbaren Eigenschaftgen werden mit dem Schlüsselwort readonly bezeichnet. Eine readonly-Eigenschaft muss entweder direkt oder spätestens im Konstruktor initialisiert werden:
Die Eigenschaft born wurde als readonly deklariert. Sie wird nicht mehr direkt initialisiert, sondern erhält im Konstruktor einen Wert. Die Eigenschaft kann nun nicht mehr überschrieben werden:
Sichtbarkeit (Visibility)
Klassen dienen der Kapselung. Hierzu ist es sinnvoll, wenn die Daten nicht einfach beliebig von aussen modifizierbar sind. Hierzu können Eigenschaften und Methoden mit einer bestimmten Sichtbarkeit versehen werden. Hierzu gibt es drei Optionen:
| Sichtbarkeit | Zugriff auf Eigenschaft/Methode |
|---|---|
public | von aussen möglich |
private | nur von innerhalb der Klasse möglich |
protected | von innerhalb derselben und erbenden Klassen möglich |
Das protected-Schlüsselwort wird beim Thema Vererbung genauer angeschaut. Hier sollen nur public und private von Interesse sein.
Wird keine Sichtbarkeit angegeben, gilt standardmässig public.
Die Eigenschaften der Klasse Person sollen privat gemacht werden. Bei den Methoden soll show() explizit öffentlich bleiben, während formatYMD nur noch von innen sichtbar, d.h. private sein soll.
TypeScript erlaubt es, die Eigenschaften gleich in der Parameterliste des Konstruktors zu definieren, indem man das Schlüsselwort private (bzw. public) dort aufführt:
Der Direktzugriff auf die Eigenschaften ist nun nicht mehr erlaubt:
Info
Die Sichtbarkeiten public, private und protected sind TypeScript-Features. In JavaScript können Eigenschaften mit dem Präfix # ebenfalls privat gemacht werden:
Das Präfix # wird dadurch zum Teil des Namens der Eigenschaft! Im Modul 320 soll dieser Mechanismus nicht zum Einsatz kommen. Es sollen stattdessen die TypeScript-Sichtbarkeiten verwendet werden.
Akzessoren (Accessors: Getter & Setter)
Oftmals soll der Zugriff auf Datenelemente nicht ganz verboten sondern eingeschränkt werden. So möchte man beim Setzen eines Wertes eine Validierung des neuen Wertes durchführen, oder Berechnungen beim Auslesen eines Wertes durchführen. Zwar ist dies auch mit Methoden möglich, im Gebrauch sind aber sogenannte Akzessoren (engl. Accessors) etwas ergonomischer.
Man unterscheidet zwischen get- und set-Akzessoren. Für eine Eigenschaft können beide Varianten oder nur eine davon umgesetzt werden:
Akzessoren dürfen nicht den gleichen Namen wie Eigenschaften tragen. Ihr Zugriff erfolgt ohne runde Klammern:
Lebenszyklen (statisch und instanzgebunden)
Eine Klasse definiert einen Datentyp. Das Schlüsselwort this greift nicht auf die Klasse zu, sondern auf die jeweilige Instanz der Klasse, d.h. auf ein spezifisches Objekt. Diese Instanzen entstehen aber erst durch die Initialisierung.
Es ist aber auch möglich, eine Klasse mit Daten auszustatten, die nicht für einzelne Instanzen gelten, sondern für die Klasse selber. Solche Eigenschaften bezeichnet man als statisch:
Die Eigenschaft count wird mit dem Schlüsselwort static deklariert und mit dem Wert 0 initialisiert. Im Konstruktor wird der Zähler inkrementiert. Dabei erfolgt der Zugriff über den Klassennamen Person, nicht über this, denn count ist an die Klasse und nicht an eine Instanz gebunden!
Der Getter numberOfPeople ist ebenfalls als statisch deklariert. Auch Methoden können statisch deklariert werden.
Der Zugriff erfolgt wiederum über den Klassennamen und nicht über den Objektnamen:
Statische Eigenschaften und Methoden sind immer dann sinnvoll, wenn man Informationen verwalten möchte, die nicht an eine bestimmte Instanz gebunden sind. Erinnern wir uns an die Methode formatYMD:
Da diese nur auf dem Argument d arbeitet und keinerlei Referenzen auf konkrete Instanzen mit this aufweist, könnte diese Methode auch als statisch definiert werden:
Der Zugriff auf formatYMD aus der Methode show() heraus darf aber dann nicht mehr mit this, sondern nur noch über den Klassennamen Person erfolgen:
Statische Methoden sind immer dann sinnvoll, wenn eine Funktion nur im Kontext einer bestimmten Klasse verwendet wird, aber nicht auf die Instanz zugreift, sondern nur auf ihre übergebenen Argumente – und auf statische Eigenschaften oder Akzessoren.