Signatur
Beschreibung
Private Elemente sind die gekapselten Gegenstücke zu den öffentlichen Klassen-Elementen. Man deklariert sie mit einem vorangestellten #, z. B. #count oder #validate(). Das gibt es für Instanzfelder, Instanzmethoden, Getter/Setter und für alle drei auch als static-Variante. Die Sprache selbst setzt diese Privatsphäre durch („hard private“). Anders als bei der Konvention mit einem Unterstrich (_count) kommt man von außerhalb des Klassenkörpers weder lesend noch schreibend an diese Elemente heran.
Ein Zugriff von außen wie obj.#count ist ein Syntaxfehler, der schon beim Parsen auffällt. Greift man innerhalb der Klasse auf ein privates Element eines Objekts zu, das es nicht besitzt, wird ein TypeError geworfen. Mit der Prüfung #feld in obj (ergonomische Markenprüfung, engl. „brand check“) lässt sich sicher testen, ob ein Objekt von der eigenen Klasse erzeugt wurde.
Private Elemente eignen sich überall dort, wo interner Zustand oder Hilfslogik vor Nutzern einer Klasse verborgen werden soll: Invarianten schützen (z. B. Kontostand nur über Methoden änderbar), Implementierungsdetails ohne Breaking Changes austauschen, Namenskollisionen mit Unterklassen vermeiden. Private Namen sind pro Klassendefinition eindeutig. Eine Unterklasse kann ein gleichnamiges #feld deklarieren, ohne das der Oberklasse zu berühren, und kann auf die privaten Elemente der Oberklasse auch nicht zugreifen.
Private Elemente sind keine Eigenschaften im üblichen Sinn. Sie tauchen weder in Object.keys(), JSON.stringify() und Reflect.ownKeys() noch beim Spread-Operator auf, und Proxy-Traps sehen sie nicht.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $#name Pflicht | PrivateIdentifier | Name des privaten Elements, immer mit #-Präfix. Das # gehört fest zum Namen: #a und a sind zwei verschiedene Elemente. |
|
| $wert | any | undefined | Optionaler Initialisierer eines privaten Feldes. Er wird bei jeder Instanziierung ausgewertet (bei static einmal beim Auswerten der Klasse). |
| $static | Modifikator | Macht das private Element zu einem Element der Klasse selbst statt der Instanzen, z. B. static #instances = 0. |
Rückgabewert
this.#name liefert den Wert des Feldes, die Methode bzw. das Ergebnis des Getters. #name in obj liefert einen boolean.Beispiele
Gekapselter Kontostand mit privatem Feld und privater Methode
class Konto {
#saldo = 0;
#pruefeBetrag(betrag) {
if (!Number.isFinite(betrag) || betrag <= 0) {
throw new RangeError('Ungültiger Betrag');
}
}
einzahlen(betrag) {
this.#pruefeBetrag(betrag);
this.#saldo += betrag;
return this;
}
get saldo() {
return this.#saldo;
}
}
const konto = new Konto().einzahlen(100).einzahlen(50);
console.log(konto.saldo);
console.log(Object.keys(konto));
console.log(JSON.stringify(konto));
// konto.#saldo = 1e6; // SyntaxError: Private field '#saldo' must be declared in an enclosing class
Statische private Felder: Instanzzähler und Singleton
class Config {
static #instanz = null;
static #erzeugt = 0;
#werte;
constructor(werte) {
this.#werte = { ...werte };
Config.#erzeugt++;
}
static holen() {
Config.#instanz ??= new Config({ theme: 'dark' });
return Config.#instanz;
}
static get anzahl() {
return Config.#erzeugt;
}
get(key) {
return this.#werte[key];
}
}
const a = Config.holen();
const b = Config.holen();
console.log(a === b, a.get('theme'), Config.anzahl);
Markenprüfung mit #feld in obj
class Punkt {
#x;
#y;
constructor(x, y) {
this.#x = x;
this.#y = y;
}
static istPunkt(obj) {
return #x in obj;
}
equals(other) {
if (!Punkt.istPunkt(other)) return false;
return this.#x === other.#x && this.#y === other.#y;
}
}
const p = new Punkt(1, 2);
console.log(p.equals(new Punkt(1, 2)));
console.log(p.equals({ x: 1, y: 2 }));
console.log(Punkt.istPunkt(p));
Privater Accessor mit Validierung
class Temperatur {
#celsius = 0;
get #kelvin() {
return this.#celsius + 273.15;
}
set #wert(c) {
if (c < -273.15) throw new RangeError('Unter dem absoluten Nullpunkt');
this.#celsius = c;
}
setzen(c) {
this.#wert = c;
return `${c} °C = ${this.#kelvin.toFixed(2)} K`;
}
}
console.log(new Temperatur().setzen(25));
// Wichtig · Fallstricke
- Deklarationspflicht: Jedes private Element muss im Klassenkörper deklariert sein.
this.#neu = 1ohne vorherige Deklaration ist einSyntaxError. Private Felder lassen sich nicht dynamisch anlegen und nicht mitdeleteentfernen. - Kein dynamischer Zugriff:
this['#feld']spricht eine ganz normale öffentliche Eigenschaft namens"#feld"an, nicht das private Element. Eine Schreibweise wiethis[#feld]gibt es nicht. - TypeError bei fremden Objekten: Wird eine Methode mit fremdem
thisaufgerufen, etwa übercalloder bei einem losgelösten Callback, wirftthis.#xeinenTypeError. Hier helfen#x in objoderbind. - Proxy: Auf einem
Proxyum eine Instanz schlägt der Zugriff auf private Elemente fehl, denn der Proxy ist nicht das Zielobjekt. Das betrifft z. B. Reaktivitätssysteme wie Vue. - Statische private Elemente und Vererbung:
this.#statischin einer statischen Methode wirft einenTypeError, wenn die Methode über eine Unterklasse aufgerufen wird. Deshalb besser den Klassennamen verwenden (Config.#instanz). - Keine Kapselung pro Instanz: Privat heißt privat pro Klasse. Methoden einer Klasse dürfen auf
other.#xanderer Instanzen derselben Klasse zugreifen. - Kompatibilität: Private Felder, Methoden und Accessoren funktionieren in allen modernen Browsern (Chrome 84+, Firefox 90+, Safari 15+) und ab Node.js 14.6.
#x in objbraucht Chrome 91+, Firefox 90+, Safari 15+ bzw. Node.js 16.4+. Für ältere Umgebungen übersetzen Babel oder TypeScript den Code, meist mitWeakMap. - Das TypeScript-Schlüsselwort
privatewirkt nur zur Übersetzungszeit. Echte Privatsphäre zur Laufzeit bietet nur#.