Das Problem mit dem Unterstrich
JavaScript hatte jahrelang keine privaten Felder. Die gängige Konvention war, einer Eigenschaft einen Unterstrich voranzustellen – in der Hoffnung, dass sich alle daran halten:
_count sieht privat aus, ist es aber nicht. Jeder Aufrufer kann den Wert lesen, überschreiben oder löschen. Der Unterstrich ist ein höflicher Hinweis an der Tür – die Tür selbst steht sperrangelweit offen.
Modernes JavaScript hat das mit echten Private Fields behoben, die mit # gekennzeichnet werden.
Mit # wird das Feld wirklich privat
Schreib einfach # vor den Feldnamen – sowohl bei der Deklaration als auch bei jedem Zugriff:
Innerhalb der Klasse funktioniert this.#count ganz normal. Von außen ist das Feld schlicht nicht existent:
Das # ist buchstäblich Teil des Feldnamens. Es ist kein Modifier-Keyword wie private in anderen Sprachen – sondern ein Sigil, über das der Parser einen separaten, geschützten Slot am Objekt nachschlägt. Deshalb taucht der Fehler schon beim Parsen auf, noch bevor der Code überhaupt läuft.
Private Methoden und Getter
Nicht nur Felder lassen sich als privat kennzeichnen. Auch Methoden, Getter und Setter akzeptieren das #-Präfix:
#assertPositive ist ein interner Helfer. Er gehört nicht zur öffentlichen API, und wenn man ihn wirklich privat macht, ruft ihn auch garantiert niemand versehentlich von außen auf – und niemand kann sich darauf verlassen. Dadurch bleibst du flexibel, falls du ihn später umbenennen oder entfernen willst.
Private statische Member
Auch statische Member lassen sich privat machen. Einfach dasselbe # davorstellen:
Private statics gehören zur Klasse selbst, nicht zu den Instanzen. Praktisch für Zähler, Caches oder Konfiguration, die die Klasse gar nicht erst verlassen soll.
Unterklassen haben keinen Zugriff darauf
An dieser Stelle stolpern vor allem Entwickler, die aus Java oder C# kommen. Private Fields in JavaScript sind nämlich class-private, nicht instance-private. Eine Unterklasse kommt also nicht an die privaten Felder der Elternklasse heran:
In JavaScript gibt es kein protected. Wenn eine Unterklasse auf die Daten zugreifen muss, muss die Elternklasse eine Methode, einen Getter oder (seltener) ein nicht-privates Feld bereitstellen. Das ist bewusst so gebaut: Private heißt wirklich privat, und Vererbung reißt keine Löcher in die Kapselung.
Mit in prüfen, ob ein Private Field existiert
Manchmal willst du sicherstellen, dass ein Objekt tatsächlich zu deiner Klasse gehört — ein sogenannter Brand Check. Der in-Operator funktioniert innerhalb der Klasse auch mit Private-Field-Namen:
Da nur Wallet Objekte mit #balance erzeugen kann, ist #balance in obj ein zuverlässiger Check dafür, ob obj tatsächlich eine echte Wallet-Instanz ist. In manchen Grenzfällen ist das schneller und sicherer als instanceof, denn private Fields lassen sich von außen nicht fälschen.
Typische Stolperfalle: Plain Objects haben keine Private Fields
Private Fields existieren nur auf Instanzen, die vom Klassen-Constructor erzeugt wurden. Wenn du versuchst, ein Private Field auf einem Objekt zu verwenden, das nicht mit new erzeugt wurde, fliegt dir ein Fehler um die Ohren:
Ein Aufruf der Methode mit einem this, das kein Point ist, wirft zur Laufzeit einen Fehler. Genau das ist der Mechanismus hinter dem oben gezeigten Brand Check: Private Fields sind an die Klasse gebunden, die sie deklariert hat – nicht an irgendein Objekt, das zufällig passend aussieht.
Wann man zu # greifen sollte
Setze Private Fields standardmäßig überall dort ein, wo State oder eine Hilfsmethode nicht Teil der öffentlichen API deiner Klasse ist. Die Gründe:
- Freiheit beim Refactoring. Aufrufer können sich nicht auf Interna verlassen, die sie schlichtweg nicht sehen.
- Echte Kapselung. Kein versehentliches Lesen, Schreiben oder Löschen von außen.
- Aufgeräumtere Autovervollständigung. Editoren schlagen private Member externen Aufrufern gar nicht erst vor.
Öffentliche Properties nutzt du dann, wenn etwas tatsächlich zum Interface gehört. Für Nur-Lese-Zugriff auf ein Private Field bietet sich ein Getter an (get name()). Und die alte Unterstrich-Konvention kannst du dir sparen – sie war ein Workaround für eine Lücke, die die Sprache inzwischen selbst geschlossen hat.
#celsius ist der versteckte Speicher; celsius und fahrenheit sind nur lesbare Views darauf. Von außen lässt sich der interne Zustand nicht manipulieren, und die Klasse kann später jederzeit ändern, wie der Wert intern abgelegt wird.
Weiter geht's: Prototypes
Klassen sind im Grunde nur syntaktischer Zucker über dem Prototype-System von JavaScript – dem älteren, fundamentaleren Modell, auf dem die Sprache tatsächlich aufgebaut ist. Wer Prototypes versteht, begreift auch, warum sich this so verhält, wie es sich verhält, wie Vererbung wirklich funktioniert und was bei extends unter der Haube eigentlich passiert. Darum geht es auf der nächsten Seite.
Häufig gestellte Fragen
Wie deklariert man ein privates Feld in JavaScript?
Du stellst dem Feldnamen einfach ein # voran – und zwar bei der Deklaration und bei jedem einzelnen Zugriff. class Counter { #count = 0; increment() { this.#count++; } } legt ein echtes privates Feld an. Wichtig: Das # gehört zum Namen, es ist kein Operator.
Was ist der Unterschied zwischen #field und _field in JavaScript?
#field und _field in JavaScript?_field ist nur eine Namenskonvention – die Property bleibt public und jeder kann sie lesen oder überschreiben. #field wird dagegen von der Sprache selbst erzwungen: Code außerhalb der Klasse kommt schlicht nicht dran, und der Versuch führt schon beim Parsen zu einem SyntaxError. Wenn du echte Privatheit willst, nimm #.
Können Subklassen auf private Felder der Elternklasse zugreifen?
Nein. Private Fields sind an die Klasse gebunden, die sie deklariert – selbst Subklassen haben keinen Zugriff. Braucht die Subklasse trotzdem Zugriff, muss die Elternklasse eine Methode oder einen Getter anbieten. Das ist strenger als protected in anderen Sprachen, und genau so ist es auch gewollt.
Kann ich prüfen, ob ein Objekt ein bestimmtes privates Feld hat?
Ja, mit dem in-Operator innerhalb der Klasse: #field in obj gibt true zurück, wenn obj dieses private Feld besitzt. Praktisch für sogenannte Brand Checks – also um sicherzustellen, dass ein Objekt wirklich eine Instanz deiner Klasse ist, bevor du Methoden darauf aufrufst.