EMZETT.
Login

Kurz: Der Gültigkeitsbereich (Scope) bestimmt, wo eine Variable sichtbar ist.

Teil des Kurses JavaScript

Kapitel 12 von 40 im Kurs JavaScript. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Der Gültigkeitsbereich (Scope) bestimmt, wo eine Variable sichtbar ist.

Arten von Scope

ScopeGilt inErzeugt durch
globalim ganzen ProgrammDeklaration außerhalb von allem
Funktionin der Funktionfunction
Blockzwischen { }let, const in if, for usw.
Modulin der DateiES-Module
const global = "überall";
function f() {
  const lokal = "nur hier";
  if (true) {
    const imBlock = "nur im Block";
    console.log(global, lokal, imBlock);
  }
  try { console.log(imBlock); } catch (e) { console.log(e.name); }
}
f();
try { console.log(lokal); } catch (e) { console.log(e.name); }

Ausgabe:

überall nur hier nur im Block
ReferenceError
ReferenceError

Innere Bereiche sehen äußere Variablen, nicht umgekehrt. Findet JavaScript einen Namen nicht im aktuellen Bereich, sucht es nach außen weiter (Scope-Kette).

Verdecken (Shadowing)

const x = "außen";
function g() {
  const x = "innen";        // verdeckt das äußere x
  return x;
}
console.log(g(), x);

Ausgabe:

innen außen

Globale Variablen vermeiden

Globale Variablen kann überall jemand ändern. Halte Daten so lokal wie möglich.

Closures

Eine Closure entsteht, wenn eine innere Funktion Variablen der äußeren Funktion “mitnimmt”, auch nachdem die äußere längst fertig ist:

function zaehlerErstellen() {
  let stand = 0;
  return function () {
    stand++;
    return stand;
  };
}
const a = zaehlerErstellen();
const b = zaehlerErstellen();
console.log(a(), a(), a());
console.log(b());

Ausgabe:

1 2 3
1

stand ist von außen nicht erreichbar, nur über die zurückgegebene Funktion. Jeder Aufruf von zaehlerErstellen erzeugt einen eigenen stand. Closures dienen als private Variablen.

Praktisch: Funktionen mit Gedächtnis

function einmalig(fn) {
  let schonGelaufen = false, ergebnis;
  return (...args) => {
    if (!schonGelaufen) { ergebnis = fn(...args); schonGelaufen = true; }
    return ergebnis;
  };
}
const init = einmalig(() => { console.log("initialisiere"); return 42; });
console.log(init(), init());
 
function cache(fn) {
  const gemerkt = new Map();
  return n => gemerkt.has(n) ? gemerkt.get(n) : (gemerkt.set(n, fn(n)), gemerkt.get(n));
}
const langsamQuadrat = n => { console.log("rechne", n); return n * n; };
const schnell = cache(langsamQuadrat);
console.log(schnell(9), schnell(9));

Ausgabe:

initialisiere
42 42
rechne 9
81 81

Die berühmte Schleifenfalle mit var

const mitVar = [];
for (var i = 0; i < 3; i++) mitVar.push(() => i);
const mitLet = [];
for (let j = 0; j < 3; j++) mitLet.push(() => j);
console.log(mitVar.map(f => f()), mitLet.map(f => f()));

Ausgabe:

[ 3, 3, 3 ] [ 0, 1, 2 ]

Mit var teilen sich alle Funktionen dieselbe Variable i (am Ende 3). Mit let bekommt jeder Durchlauf eine eigene Variable.

Module als Gültigkeitsbereich

Seit ES-Modulen hat jede Datei ihren eigenen Scope; nur was du mit export freigibst, ist außen sichtbar (eigenes Kapitel).

Merke

  • Scope-Kette: Von innen nach außen wird nach Namen gesucht
  • let und const gelten im Block, var in der Funktion
  • Eine Closure behält Zugriff auf die Variablen ihrer Entstehung
  • Closures erzeugen private Zustände, Caches und Fabriken
  • Globale Variablen vermeiden

Übungsaufgabe

Baue mit einer Closure einen Zähler mit den Methoden erhoehen(), verringern() und wert(), dessen Stand von außen nicht direkt veränderbar ist.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Arrow-Funktionen

Weiter: Arrays

Alle Kapitel: JavaScript im Überblick