HOWTO · JavaScript

Statische Variablen in JavaScript

In diesem Tutorial werden statische Variablen in JavaScript vorgestellt.

Auf dieser Seite

JavaScript kann Werte zwischen Funktionsaufrufen beibehalten: mit einem öffentlichen statischen Klassenfeld, einer Eigenschaft eines Funktionsobjekts oder einer lokalen Variable, die durch eine Closure erhalten bleibt. Alle drei Muster speichern Zustand, unterscheiden sich aber darin, wo dieser Zustand liegt und wer darauf zugreifen kann. Verwenden Sie ein Klassenfeld für gemeinsam genutzten Zustand einer Klasse, eine Funktionseigenschaft für einfachen öffentlichen Zustand auf Funktionsebene oder eine Closure, wenn Aufrufer den Wert verwenden, die zugehörige Bindung aber nicht direkt lesen oder ändern sollen.

Was eine statische Variable in JavaScript bedeutet

Eine statische Variable ist ein Zustand, der einer Funktion oder Klasse zugeordnet ist und nicht einem einzelnen Funktionsaufruf. JavaScript bietet keine universelle Deklaration für statische Variablen in beliebigen Funktionen. Stattdessen gibt es mehrere Muster. Das Schlüsselwort static deklariert ein Klassenfeld oder eine Klassenmethode direkt in der Klasse. Eine einer Funktion zugewiesene Eigenschaft gehört ebenfalls zu dem jeweiligen Funktionsobjekt. Eine Closure hält eine lokale Bindung für eine innere Funktion verfügbar, nachdem die äußere Funktion zurückgekehrt ist.

Diese Bedeutungen hängen zusammen, doch die Mechanismen sind nicht in jeder Hinsicht austauschbar. Auf Klassenfelder und Funktionseigenschaften kann über das zugehörige Klassen- oder Funktionsobjekt zugegriffen werden. Eine Closure kann ihre Bindung privat halten und nur die von Ihnen ausgewählten Operationen bereitstellen.

Ein statisches Klassenfeld für klassenweiten Zustand verwenden

Ein öffentliches statisches Klassenfeld gehört zur Klasse. Daher greift jede Instanz über den Klassennamen auf dasselbe Feld zu; in die einzelnen Instanzen wird es nicht kopiert. Diese Syntax setzt eine Laufzeitumgebung voraus, die öffentliche statische Klassenfelder unterstützt. Falls Ihre Laufzeitumgebung dies nicht unterstützt, verwenden Sie stattdessen das nachfolgende Muster mit einer Funktionseigenschaft oder einer Closure.

Führen Sie dieses Beispiel aus:

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

class VisitCounter {
  static count = 0;

  static recordVisit() {
    return ++VisitCounter.count;
  }
}

console.log(VisitCounter.recordVisit());
console.log(VisitCounter.recordVisit());
console.log(VisitCounter.count);
1
2
2

Beide Aufrufe aktualisieren dasselbe VisitCounter.count. Da das Feld öffentlich ist, kann anderer Code es auch als VisitCounter.count lesen oder zuweisen. Verwenden Sie ein privates statisches Feld (mit einem #-Namen), wenn die Klasse selbst den Zugriff kontrollieren soll. Auf ein statisches Element wird über die Klasse zugegriffen, nicht über eine Instanz wie new VisitCounter().

Einer Funktion eine Eigenschaft zuweisen

Funktionen sind Objekte. Daher kann eine benannte Funktion eine Eigenschaft besitzen, deren Wert über mehrere Aufrufe hinweg erhalten bleibt. Das ist eine kompakte Möglichkeit für einen Zähler oder Cache, der zu einer einzelnen Funktion gehört. Die Eigenschaft ist öffentlich: Anderer Code kann nextId.counter auslesen oder ändern. Dieses Muster bietet also keinen Schutz der Eigenschaft.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

function nextId() {
  nextId.counter = (nextId.counter ?? 0) + 1;
  return nextId.counter;
}

console.log(nextId());
console.log(nextId());
console.log(nextId.counter);
1
2
2

Dasselbe Prinzip gilt für eine Konstruktorfunktion. Eine Eigenschaft wie Widget.count gehört zur Konstruktorfunktion, während widget.name zu einer einzelnen Instanz gehört. Jede Instanz erhält keine eigene Kopie von Widget.count; auf diese gemeinsam genutzte Eigenschaft wird über Widget zugegriffen. Die Zuordnung ähnelt klassenweitem Zustand, doch Funktionseigenschaften in JavaScript bieten nicht alle Zugriffskontrollen oder Typgarantien, die manche Klassensysteme bereitstellen.

Eine Closure für privaten Zustand verwenden

Mit einer Closure kann eine innere Funktion weiterhin eine lokale Bindung verwenden, nachdem die äußere Funktion beendet wurde. Ein IIFE (sofort ausgeführter Funktionsausdruck) erzeugt diesen äußeren Gültigkeitsbereich unmittelbar und gibt eine Operation zurück, die den erhaltenen Wert aktualisieren kann. Da die Bindung selbst nicht in einem öffentlichen Objekt abgelegt wird, kann Code von außen sie weder direkt lesen noch zuweisen.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

const counter = (function () {
  let count = 0;
  return function () {
    return ++count;
  };
})();

console.log(counter());
console.log(counter());
1
2

Hier greift counter auf die Bindung count zu. Jeder Aufruf von counter() erhöht denselben erhaltenen Wert; die Aufrufe geben daher zuerst 1 und dann 2 aus. Der Wert bleibt privat, während counter die öffentliche Schnittstelle bildet. Für unabhängige Zustände rufen Sie eine separate Factory-Funktion auf, die eine neue Closure erzeugt und zurückgibt. Ein Aufruf des zurückgegebenen counter erhöht dagegen nur dessen bestehenden Zustand. Wenn Aufrufer den Zähler zurücksetzen oder auslesen können sollen, geben Sie ein kleines Objekt mit ausdrücklichen Methoden zurück, statt die Bindung selbst offenzulegen.

Warum Sie arguments.callee vermeiden sollten

arguments.callee verweist über das Arguments-Objekt auf die gerade ausgeführte Funktion. Ältere Muster verwendeten es, um dieser Funktion eine Eigenschaft zuzuweisen, ähnlich wie bei einer benannten Funktionseigenschaft. Es handelt sich um eine veraltete Technik, die im Strict Mode verboten ist. Code, der sie verwendet, kann daher fehlschlagen, sobald der Strict Mode gilt, auch in ECMAScript-Modulen. Benannte Funktionen machen den Zusammenhang deutlicher; Closures ermöglichen privaten Zustand, ohne auf diese eingeschränkte Eigenschaft zurückzugreifen. Verwenden Sie lieber eines der unterstützten Muster oben, statt neuen Code auf arguments.callee aufzubauen.