Zum Inhalt springen
aviral gupta

// I2.4 · ca. 34 Min. · Aufbau

Private Felder, Getter und statische Member

Nach dieser Lektion schützen Sie die Daten einer Klasse mit #privaten Feldern, bieten sie über Getter und Setter an, die neue Werte prüfen, und ergänzen statische Methoden wie eine Factory.

Lektion 4 von 5 in I2 Objekte, Prototypen und Klassen

Danach können Sie

  • Daten in #privaten Feldern und Methoden verbergen und mit #x in obj auf sie prüfen
  • Berechnete und geprüfte Werte über get- und set-Accessoren anbieten
  • Statische Methoden und Felder schreiben, auch eine statische Factory-Methode
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen, aus Lektion I2.3: Was gibt dieser Code aus?

    class Base {
      hello() {
        return "base";
      }
    }
    class Child extends Base {
      hello() {
        return `child + ${super.hello()}`;
      }
    }
    console.log(new Child().hello());
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: Jemand setzt c.count von außen. Was gibt dieser Code aus?

    class Counter {
      #count = 0;
      increment() {
        this.#count += 1;
        return this.#count;
      }
    }
    const c = new Counter();
    c.increment();
    c.count = 100;
    console.log(c.increment(), Object.keys(c).join(","));
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie das Schlüsselwort, damit cart.total wie eine Eigenschaft gelesen wird, ohne Klammern.

    class Cart {
      #prices = [3, 4];
      ____ total() {
        return this.#prices.reduce((sum, price) => sum + price, 0);
      }
    }
    console.log(new Cart().total);
    #prices = [3, 4];total() {
  4. Üben · Aufgabe 4 von 7

    Ordnen Sie jedem Teil einer Klasse zu, was er Ihnen bringt.

  5. Üben · Aufgabe 5 von 7

    Eine statische Factory. Was gibt dieser Code aus?

    class Temp {
      constructor(celsius) {
        this.celsius = celsius;
      }
      static fromFahrenheit(f) {
        return new Temp(((f - 32) * 5) / 9);
      }
    }
    const t = Temp.fromFahrenheit(212);
    console.log(t.celsius, typeof t.fromFahrenheit, typeof Temp.fromFahrenheit);
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Zwei Arten, nach #id zu fragen. Was gibt dieser Code aus?

    class Ticket {
      #id;
      constructor(id) {
        this.#id = id;
      }
      static isTicket(value) {
        return #id in value;
      }
    }
    const fake = {id: 1};
    console.log(Ticket.isTicket(new Ticket(1)), Ticket.isTicket(fake), "#id" in new Ticket(2));
  7. Anwenden · Aufgabe 7 von 7

    Kleine Aufgabe: Schreiben Sie eine Klasse Rating, die ihre Sterne in einem privaten Feld #stars hält. Ein Setter stars akzeptiert nur ganze Zahlen von 1 bis 5 und wirft sonst einen RangeError; auch der constructor verwendet ihn. Ein Getter stars gibt den Wert zurück, ein Getter label gibt einen Text wie "4 of 5" zurück. Ergänzen Sie ein statisches average(ratings). Zeigen Sie, dass ein falscher Wert abgelehnt und der alte behalten wird.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Ein Konto, das seinen Kontostand bewacht

Account hält den Inhaber und den Kontostand in privaten Feldern. Ein Setter bereinigt und prüft den Inhaber, ein Getter macht den Kontostand schreibgeschützt, und das private #check(amount) lehnt Beträge ab, die nicht positiv sind. Statische Member gehören zur Klasse: eine Währung, ein Zähler, eine Factory, die ein Konto aus JSON baut, und isAccount, das #balance in value verwendet. Die Schleife probiert vier verbotene Dinge aus.

main.js

class Account {
  static currency = "EUR";
  static count = 0;

  #owner;
  #balance = 0;

  constructor(owner) {
    this.owner = owner; // runs the setter below
    Account.count += 1;
  }

  static fromJSON(text) {
    const data = JSON.parse(text);
    const account = new Account(data.owner);
    account.deposit(data.balance);
    return account;
  }

  static isAccount(value) {
    return #balance in value;
  }

  get owner() {
    return this.#owner;
  }

  set owner(name) {
    if (typeof name !== "string" || name.trim() === "") {
      throw new TypeError("owner must be a non-empty string");
    }
    this.#owner = name.trim();
  }

  get balance() {
    return this.#balance;
  }

  deposit(amount) {
    this.#check(amount);
    this.#balance += amount;
  }

  withdraw(amount) {
    this.#check(amount);
    if (amount > this.#balance) throw new RangeError("not enough money");
    this.#balance -= amount;
  }

  #check(amount) {
    if (!(amount > 0)) throw new RangeError(`invalid amount: ${amount}`);
  }
}

const ada = new Account("  Ada ");
ada.deposit(100);
ada.withdraw(30);
console.log(ada.owner, ada.balance, Account.currency);
console.log(ada, Object.keys(ada));

for (const attempt of [() => (ada.balance = 1000000), () => ada.withdraw(500), () => ada.deposit(-5), () => (ada.owner = " ")]) {
  try {
    attempt();
  } catch (error) {
    console.log(`${error.name}: ${error.message}`);
  }
}

const max = Account.fromJSON('{"owner": "Max", "balance": 50}');
console.log(max.owner, max.balance, Account.count);
console.log(Account.isAccount(max), Account.isAccount({balance: 50}));
console.log(typeof ada.fromJSON);

Ausführen mit

node main.js

Ausgabe

Ada 70 EUR
Account {} []
TypeError: Cannot set property balance of #<Account> which has only a getter
RangeError: not enough money
RangeError: invalid amount: -5
TypeError: owner must be a non-empty string
Max 50 2
true false
undefined
  • Der Setter hat " Ada " zu "Ada" gekürzt; this.owner = owner im constructor lief schon durch ihn.
  • Node.js gibt das Konto als Account {} aus, und Object.keys findet nichts: Alle seine Daten sind privat.
  • balance hat nur einen Getter, die Zuweisung warf also einen TypeError, und der Kontostand blieb 70.
  • Account.count zählt jedes Konto, auch das, das fromJSON gebaut hat.
  • Ein einfaches Objekt mit einer Eigenschaft balance ist kein Account: Es hat kein #balance.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 2

Eine Temperatur, die das Unmögliche ablehnt

Temperature soll ihren Wert in einem privaten Feld #celsius halten, hinter einem Getter und einem Setter namens celsius. Der Setter wirft für Werte unter -273.15 einen RangeError mit der Meldung below absolute zero, auch wenn der constructor einen solchen Wert bekommt. Der Getter fahrenheit funktioniert schon. Der Starter speichert celsius als öffentliche Eigenschaft und prüft nichts.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Führen Sie die Tests aus: -300 wird akzeptiert, und Object.keys findet celsius. Wo sollte der Wert stattdessen liegen?

  2. Hinweis 2

    Deklarieren Sie #celsius; im Klassenrumpf. get celsius() gibt es zurück; set celsius(value) prüft value und speichert es dann.

  3. Hinweis 3

    Im Setter: if (value < -273.15) throw new RangeError("below absolute zero"); this.#celsius = value;

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export class Temperature {
  #celsius;

  constructor(celsius) {
    this.celsius = celsius;
  }

  get celsius() {
    return this.#celsius;
  }

  set celsius(value) {
    if (value < -273.15) throw new RangeError("below absolute zero");
    this.#celsius = value;
  }

  get fahrenheit() {
    return (this.celsius * 9) / 5 + 32;
  }
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export class Temperature {
  constructor(celsius) {
    this.celsius = celsius;
  }

  get fahrenheit() {
    return (this.celsius * 9) / 5 + 32;
  }
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {Temperature} from './main.js';

test('celsius und fahrenheit lassen sich lesen', () => {
  const t = new Temperature(100);
  assert.deepEqual([t.celsius, t.fahrenheit], [100, 212], `celsius und fahrenheit sind ${t.celsius} und ${t.fahrenheit}`);
});

test('Ein Wert unter -273.15 wirft und lässt den alten Wert bestehen', () => {
  const t = new Temperature(20);
  assert.throws(() => { t.celsius = -300; }, {name: 'RangeError', message: 'below absolute zero'}, 'das Setzen von -300 sollte einen RangeError werfen');
  assert.equal(t.celsius, 20, `nach dem abgelehnten Wert ist celsius ${t.celsius}`);
});

test('Auch der constructor lehnt -300 ab', () => {
  assert.throws(() => new Temperature(-300), {name: 'RangeError'}, 'new Temperature(-300) sollte einen RangeError werfen');
});

test('Der Wert ist keine öffentliche eigene Eigenschaft', () => {
  assert.deepEqual(Object.keys(new Temperature(5)), [], 'Object.keys sollte nichts finden: Halten Sie den Wert in #celsius');
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Übung 2 von 2

Eine statische Factory und ein statisches Feld

Color.fromHex("#ff8000") soll aus einem Hex-Text eine Color bauen: Die Paare ff, 80 und 00 sind Rot, Grün und Blau zur Basis 16. Im Starter ist fromHex eine gewöhnliche Methode, Sie bräuchten also eine Farbe, um eine Farbe zu erzeugen. Machen Sie sie statisch. Ergänzen Sie dann ein statisches Feld WHITE, eine Color mit 255 für alle drei Teile.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Führen Sie die Tests aus: Color.fromHex ist undefined. Welches Schlüsselwort legt eine Methode an der Klasse selbst ab?

  2. Hinweis 2

    Schreiben Sie static vor fromHex(hex). Ein statisches Feld sieht so aus: static NAME = value;

  3. Hinweis 3

    static WHITE = new Color(255, 255, 255);

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export class Color {
  constructor(r, g, b) {
    this.r = r;
    this.g = g;
    this.b = b;
  }

  static WHITE = new Color(255, 255, 255);

  static fromHex(hex) {
    const part = (start) => parseInt(hex.slice(start, start + 2), 16);
    return new Color(part(1), part(3), part(5));
  }

  toString() {
    return `rgb(${this.r}, ${this.g}, ${this.b})`;
  }
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export class Color {
  constructor(r, g, b) {
    this.r = r;
    this.g = g;
    this.b = b;
  }

  fromHex(hex) {
    const part = (start) => parseInt(hex.slice(start, start + 2), 16);
    return new Color(part(1), part(3), part(5));
  }

  toString() {
    return `rgb(${this.r}, ${this.g}, ${this.b})`;
  }
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {Color} from './main.js';

test('Color.fromHex baut eine Color', () => {
  assert.equal(typeof Color.fromHex, 'function', 'Color.fromHex sollte eine statische Methode sein');
  const c = Color.fromHex('#ff8000');
  assert.ok(c instanceof Color, 'fromHex sollte eine Color zurückgeben');
  assert.equal(String(c), 'rgb(255, 128, 0)', `fromHex('#ff8000') ergab ${c}`);
});

test('fromHex ist keine Methode einer Farbe', () => {
  assert.equal(typeof new Color(0, 0, 0).fromHex, 'undefined', 'fromHex sollte an der Klasse liegen, nicht an jeder Farbe');
});

test('Color.WHITE ist weiß', () => {
  assert.equal(String(Color.WHITE), 'rgb(255, 255, 255)', `Color.WHITE ist ${Color.WHITE}`);
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Häufige Fehler

Ein privates Feld von außerhalb der Klasse lesen

class Account {
  #balance = 50;
}
const account = new Account();
console.log(account.#balance);

Was Node.js ausgibt

SyntaxError: Private field '#balance' must be declared in an enclosing class

Warum, und die Lösung

Ein #name existiert nur im Klassenrumpf, der es deklariert. Außerhalb ist der Code nicht einmal gültig, die ganze Datei startet also nicht, nicht nur diese Zeile. Geben Sie der Klasse einen Weg, den Wert zu teilen, etwa get balance() { return this.#balance; }, und lesen Sie account.balance.

Ein Getter, der sich selbst liest

class Cart {
  constructor(prices) {
    this.prices = prices;
  }
  get total() {
    return this.total ?? this.prices.reduce((sum, p) => sum + p, 0);
  }
}
console.log(new Cart([3, 4]).total);

Was Node.js ausgibt

RangeError: Maximum call stack size exceeded

Warum, und die Lösung

In get total() liest this.total die Eigenschaft total erneut, was denselben Getter aufruft, immer wieder, bis der Aufrufstapel voll ist. Ein Getter muss etwas anderes lesen: den Wert berechnen oder in einem Feld mit anderem Namen halten, meist einem privaten wie #total.

Eine statische Methode auf einer Instanz aufrufen

class Account {
  constructor(owner) {
    this.owner = owner;
  }
  static fromJSON(text) {
    return new Account(JSON.parse(text).owner);
  }
}
const ada = new Account("Ada");
const max = ada.fromJSON('{"owner": "Max"}');
console.log(max.owner);

Was Node.js ausgibt

TypeError: ada.fromJSON is not a function

Warum, und die Lösung

Eine statische Methode liegt an der Klasse, nicht an Account.prototype; Instanzen erben sie also nicht, und ada.fromJSON ist undefined. Rufen Sie sie auf der Klasse auf: Account.fromJSON('{"owner": "Max"}').

JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Private Felder und Methoden

Ein Name, der mit # beginnt, etwa #balance, ist privat: Nur Code im Klassenrumpf kann ihn lesen oder ändern. Deklarieren Sie ihn im Klassenrumpf (#balance = 0;), bevor Sie ihn verwenden. Außerhalb der Klasse ist obj.#balance nicht einmal gültiger Code: Das ganze Skript scheitert mit einem SyntaxError, bevor es läuft. Private Felder erscheinen weder in Object.keys noch in JSON.stringify, und Node.js gibt das Objekt ohne sie aus. Auch Methoden können privat sein, #check(amount), für Hilfsfunktionen, die nicht zur öffentlichen Schnittstelle gehören. #balance in obj zeigt, ob obj von dieser Klasse erzeugt wurde.

Getter und Setter

get balance() { … } läuft, wenn jemand obj.balance liest, ohne Klammern; so lässt sich ein Wert berechnen oder aus einem privaten Feld lesen. set owner(name) { … } läuft bei obj.owner = value; es kann den Wert prüfen und vor dem Speichern werfen. Ein Getter ohne Setter macht die Eigenschaft schreibgeschützt: In einem Modul oder einer Klasse wirft eine Zuweisung darauf einen TypeError. Speichern Sie den Wert unter einem anderen Namen, meist in einem #privaten Feld. Ein Getter, der seinen eigenen Namen liest, return this.total in get total(), ruft sich selbst auf, bis ein RangeError kommt.

Statische Methoden und Felder

static legt eine Methode oder ein Feld an der Klasse selbst ab, nicht an ihren Instanzen: Account.count, Account.fromJSON(text). Verwenden Sie es für Dinge, die zur ganzen Art von Objekt gehören, etwa einen Zähler, eine Einstellung oder eine Hilfsfunktion. Instanzen erreichen statische Member nicht: ada.fromJSON ist undefined. Eine statische Factory ist eine statische Methode, die eine neue Instanz baut und zurückgibt, etwa aus JSON-Text oder aus einer Hex-Farbe, sodass der constructor einfach bleiben kann. In einer statischen Methode ist this die Klasse, auf der sie aufgerufen wurde.

Quellen

Zuletzt geprüft am 5. Oktober 2026