Abschnitt 5 · Lektion 6
Gültigkeitsbereich
„Warum ist das hier undefined” ist nach == gegen === die zweithäufigste Anfängerfrage, und die Antwort ist fast immer der Gültigkeitsbereich. Es lohnt sich, ihn einmal richtig anzusehen.
Der Block ist die Grenze
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const draussen = "überall sichtbar";
if (true) {
const drinnen = "nur im if";
console.log("Im Block sehe ich:", draussen, "und", drinnen);
}
console.log("Außerhalb sehe ich:", draussen);
// Der Zähler einer Schleife gehört ebenfalls in ihren Block.
for (let i = 1; i <= 3; i++) {
console.log("i in der Schleife:", i);
}
// Diese Zeile ist der Grund für die Fehlermeldung darunter:
// drinnen und i gibt es hier nicht mehr.
console.log("i nach der Schleife:", i); Ein Block sind zwei geschweifte Klammern und alles dazwischen. Der Rumpf einer Funktion, einer Schleife, eines if, oder auch nur ein Paar Klammern für sich allein.
Was du mit let oder const in einem Block anlegst, existiert nur in diesem Block. Danach ist der Name weg, als hätte es ihn nie gegeben. Der Versuch, ihn trotzdem zu benutzen, ist ein ReferenceError, und im Beispiel siehst du genau den.
Für Schleifen heißt das: Der Zähler gehört in die Schleife. Nach der letzten Runde gibt es ihn nicht mehr. Das klingt nach einer Einschränkung und ist eine Erleichterung, denn ein Name, der nur dort existiert, wo er gebraucht wird, kann nirgendwo sonst etwas kaputt machen.
Innen sieht nach außen
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const mehrwertsteuer = 1.19;
function bruttoPreis(netto) {
// netto kennt nur diese Funktion, mehrwertsteuer kommt von außen.
const brutto = netto * mehrwertsteuer;
return brutto;
}
console.log(bruttoPreis(100));
// Dieselbe Suche noch eine Ebene tiefer.
function aussen() {
const stufe = "außen";
function innen() {
console.log("innen sieht:", stufe);
}
innen();
}
aussen(); Die Sichtbarkeit geht nur in eine Richtung.
Von innen nach außen ja: Eine Funktion sieht alles, was um sie herum steht. Findet JavaScript einen Namen nicht im aktuellen Block, schaut es eine Ebene höher, dann noch eine, bis ganz nach oben. Erst wenn er nirgends steht, gibt es einen ReferenceError.
Von außen nach innen nein: Was in einer Funktion angelegt wurde, ist außerhalb nicht zu sehen. Sonst wären zwei Funktionen mit einer Variablen namens ergebnis nicht nebeneinander möglich, und genau das ist der Punkt.
Diese Suche nach oben ist der Grund, warum Funktionen überhaupt nutzbar sind, ohne dass man ihnen alles einzeln übergibt. Sie ist auch der Grund, warum eine Variable, die ganz oben in der Datei steht, von überall aus erreichbar und veränderbar ist. Deshalb legt man dort so wenig wie möglich ab.
Der Gültigkeitsbereich als Liste
Von allen Themen dieses Kurses ist der Gültigkeitsbereich das abstrakteste, und ausgerechnet dafür gibt es ein Werkzeug, das ihn hinschreibt. Der Reiter Debug meldet an jeder Zeile, welche Namen dort sichtbar sind, und das ist wörtlich die Frage, um die es hier geht.
Zeichne das Beispiel von oben auf und klick dich durch die Schritte. In der Zeile const brutto = netto * mehrwertsteuer stehen zwei Namen in der Liste: mehrwertsteuer von ganz oben und netto aus dem Parameter. Eine Zeile weiter kommt brutto dazu. Und sobald die Funktion fertig ist, sind netto und brutto wieder weg, und es steht nur noch mehrwertsteuer da.
In der inneren Funktion des zweiten Blocks stehen beide: mehrwertsteuer und stufe. Da ist die Suche nach oben, als Liste. Und umgekehrt findest du an keiner Zeile außerhalb einen Namen, der drinnen entstanden ist.
Wenn dir eine Lektion dieses Abschnitts unklar bleibt, ist das der Ort zum Nachsehen. Der Gültigkeitsbereich ist nichts, was man auswendig lernt, sondern etwas, das man sich ein paarmal ansieht.
Was var anders macht
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> if (true) {
var mitVar = "kennt keine Blöcke";
let mitLet = "kennt Blöcke";
console.log("Im Block:", mitVar, "und", mitLet);
}
// var ist hier draußen weiterhin da. Genau das ist das Problem:
// eine Variable steht plötzlich an Stellen, an denen sie nichts
// zu suchen hat.
console.log("Nach dem Block:", mitVar);
// Noch deutlicher in einer Schleife. Der Zähler überlebt sie.
for (var j = 1; j <= 3; j++) {
// nichts zu tun
}
console.log("j nach der Schleife:", j); var ist die alte Art, eine Variable anzulegen, und sie kennt nur Funktionen als Grenze, keine Blöcke.
Eine Variable, die mit var in einem if oder einer Schleife entsteht, ist danach immer noch da. Sie ist auch schon davor da, dann allerdings mit dem Wert undefined. Beides sorgt für Fehler, die schwer zu finden sind, weil der Code an der Stelle, an der es knallt, völlig richtig aussieht.
Deshalb kommt var in diesem Kurs nicht vor. Du wirst es in älterem Code sehen, und dann weißt du jetzt, worauf zu achten ist. Schreiben musst du es nie: const für alles, was sich nicht ändert, let für den Rest.
Vor der eigenen Zeile
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> // Eine Funktionsdeklaration ist schon vor ihrer eigenen Zeile da.
console.log("Aufruf von oben:", frueh());
function frueh() {
return "geht";
}
// Bei const und let ist das anders. Der Name existiert zwar schon,
// benutzbar ist er aber erst ab seiner Zeile.
const spaet = "steht erst hier";
console.log("Nach der Zeile:", spaet);
// Diese Zeile steht bewusst am Ende, denn sie bricht ab: der Zugriff
// liegt vor der Zeile mit const.
console.log(nochNichtDa);
const nochNichtDa = "zu spät"; Eine Funktionsdeklaration kannst du aufrufen, bevor sie in der Datei steht. JavaScript liest die Datei vorher einmal durch und legt alle Funktionsdeklarationen an, bevor die erste Zeile läuft. Das nennt man Hoisting.
Bei const und let ist es anders. Der Name existiert zwar von Anfang an, benutzbar ist er aber erst ab seiner Zeile. Dazwischen liegt die temporale Totzone, und ein Zugriff darin ist ein ReferenceError mit einer ziemlich deutlichen Meldung.
Man kann sich das merken oder man kann sich das Problem sparen: Schreib Dinge vor ihrer Verwendung hin. Dann spielt keine dieser Regeln je eine Rolle.
Die Closure
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> function baueZaehler() {
let stand = 0;
return function () {
stand = stand + 1;
return stand;
};
}
const ersterZaehler = baueZaehler();
console.log(ersterZaehler(), ersterZaehler(), ersterZaehler());
// Ein zweiter Zähler hat seinen eigenen Stand und fängt bei 1 an.
const zweiterZaehler = baueZaehler();
console.log("Der zweite zählt:", zweiterZaehler());
console.log("Der erste zählt weiter:", ersterZaehler());
// An stand kommt von außen niemand heran. Es gibt keinen Namen
// dafür, außer innerhalb der zurückgegebenen Funktion.
console.log("Von außen sichtbar:", typeof stand); Zum Schluss die Sache, die aus dem Gültigkeitsbereich ein Werkzeug macht.
Eine innere Funktion sieht, was sie umgibt. Das gilt auch dann noch, wenn die äußere Funktion längst fertig ist. Sie nimmt ihre Umgebung sozusagen mit.
Im Beispiel legt baueZaehler bei jedem Aufruf ein frisches stand an und gibt eine Funktion zurück, die darauf zugreift. Jeder gebaute Zähler hat damit seinen eigenen Stand, und von außen kommt niemand heran: Es gibt keinen Namen dafür.
Das nennt man eine Closure, und sie ist der übliche Weg, wenn sich etwas über mehrere Aufrufe hinweg etwas merken soll, ohne dass es allen gehört. In der Challenge baust du genau das.
Auch hier lohnt sich eine Aufzeichnung, denn in der Console siehst du nur die Ergebnisse. Blätter zu den beiden Stellen, an denen stand = stand + 1 steht: Das ist dieselbe Zeile, aufgerufen von zwei verschiedenen Zählern, und einmal steht dort stand=0 und einmal stand=3. Zwei Speicher hinter einer Codezeile, und keiner der beiden hat einen Namen, unter dem man ihn von außen erreichen könnte. Genau deshalb steht in der letzten Zeile des Beispiels undefined.
Zum Mitnehmen
Ein Block sind zwei geschweifte Klammern. Was mit let oder const darin angelegt wird, existiert nur darin. Von innen sieht man nach außen, von außen nicht nach innen.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 5 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.