Abschnitt 14 · Lektion 1
Die Fehlerarten
CSS schweigt, wenn etwas nicht stimmt. Du schreibst colr: red, die Zeile wird ignoriert, und niemand sagt dir warum.
JavaScript ist da anders. Es sagt ziemlich genau, was los ist, und es sagt es sofort. Der Preis dafür ist, dass die Meldung erst einmal wie eine Beschimpfung aussieht. Sie ist aber keine, sondern die kürzeste Fassung dessen, was du wissen musst.
Eine Meldung hat drei Teile: die Art des Fehlers, den Text dazu, und die Fundstelle aus Datei und Zeile. Die Art ist der wichtigste Teil, denn sie sagt dir, wo du überhaupt suchen musst.
Die Fundstelle steht rechts in der Zeile, etwa app.js:6:10, und sie ist ein Knopf: Ein Klick bringt dich im Editor genau dorthin. Die zweite Zahl ist die Spalte.
SyntaxError: die Datei ist ungültig
<p>Sieh in die Console.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} console.log("Diese Zeile steht ganz oben.");
// Hinter dem Funktionsnamen fehlt die schließende Klammer.
// Damit ist die ganze Datei ungültig.
function begruessung( {
return "Hallo";
}
console.log("Und diese hier ganz unten."); Ein SyntaxError ist keine Panne beim Ausführen, sondern beim Lesen. Der Browser sieht sich die Datei an, bevor er sie startet, und wenn sie sich nicht als gültiges JavaScript lesen lässt, startet er sie gar nicht erst.
Genau das siehst du im ersten Beispiel: In der Console steht die Meldung, und darüber hinaus passiert nichts. Beide console.log bleiben stumm, auch der ganz oben, obwohl er vor dem Fehler steht.
Das ist der Punkt, den man einmal gesehen haben muss. Wenn deine Seite sich verhält, als wäre gar kein Skript da, ist ein SyntaxError der erste Verdacht. Nicht die Zeile, an der du gerade gearbeitet hast, sondern die Datei als Ganzes.
Typische Ursachen sind immer dieselben: eine Klammer zu wenig, ein Anführungszeichen, das nicht geschlossen ist, ein Komma zu viel in einer Liste. Dein Editor markiert das meistens schon, bevor du speicherst.
ReferenceError: den Namen gibt es hier nicht
<p>Zwei Fehler, zwei verschiedene Ursachen.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} // Das try/catch drumherum ist Thema der nächsten Lektion. Hier
// sorgt es nur dafür, dass du beide Fehler nacheinander siehst:
// ohne das würde schon der erste den Rest der Datei anhalten.
const anzahl = 3;
try {
// Tippfehler im Namen. Diesen Namen gibt es nirgends.
console.log(anzhal * 2);
} catch (fehler) {
console.error(fehler);
}
try {
const nutzer = null;
// Den Namen nutzer gibt es. Er ist nur nichts, was einen
// Namen hätte.
console.log(nutzer.name);
} catch (fehler) {
console.error(fehler);
} Ein ReferenceError heißt wörtlich: Ich soll etwas nachschlagen, das es an dieser Stelle nicht gibt.
In neun von zehn Fällen ist das ein Tippfehler. Im Beispiel steht anzhal statt anzahl, und die beiden sehen sich ähnlich genug, dass man zehnmal darüber liest.
Der zehnte Fall ist der Gültigkeitsbereich aus Lektion 5.6. Der Name existiert, aber woanders: in einer anderen Funktion, in einem anderen Block. Von außen ist er dann genauso wenig da wie ein Name, den es gar nicht gibt.
Die Meldung nennt den Namen immer mit. Das ist die schnellste Suchhilfe, die du bekommst.
TypeError: der Wert kann das nicht
Ein TypeError ist die andere Hälfte. Den Namen gibt es, der Wert ist da, aber er kann nicht, was du von ihm verlangst.
Im zweiten Teil des Beispiels ist nutzer gleich null, und null hat keine Eigenschaften. Die Meldung lautet dann sinngemäß „Cannot read properties of null”, und dahinter steht in Klammern, welche Eigenschaft es war.
In der DOM-Arbeit ist das der mit Abstand häufigste Fehler. document.querySelector liefert null, wenn nichts passt, und die nächste Zeile fasst dieses null an. Lektion 10.4 hat den Fall ausführlich behandelt. Wenn du in einem DOM-Skript einen TypeError siehst, ist die erste Frage fast immer: Hat der Selektor überhaupt etwas gefunden?
RangeError, und der Rest
Ein RangeError heißt: Der Wert ist grundsätzlich richtig, aber außerhalb dessen, was erlaubt ist. new Array(-1) geht nicht, zahl.toFixed(200) geht nicht, und eine Funktion, die sich endlos selbst aufruft, endet in „Maximum call stack size exceeded”. Firefox sortiert den letzten Fall anders ein und meldet InternalError: too much recursion, gemeint ist dasselbe.
Es gibt noch ein paar weitere Arten, aber die drei oben plus RangeError decken praktisch alles ab, was dir am Anfang begegnet.
Den Stacktrace lesen
<p>Drei Funktionen, ein Fehler ganz unten.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} function ganzUnten(wert) {
return wert.toUpperCase();
}
function inDerMitte(wert) {
return ganzUnten(wert);
}
function ganzOben() {
return inDerMitte(42);
}
try {
ganzOben();
} catch (fehler) {
console.error(fehler);
} Unter der Meldung steht eine Liste von Zeilen, der Stacktrace. Sie beantwortet die Frage, wie das Programm überhaupt an die Stelle gekommen ist.
Gelesen wird sie von oben nach unten: Die oberste Zeile ist die Stelle, an der es geknallt hat. Darunter steht, wer diese Funktion aufgerufen hat, darunter wer den Aufrufer aufgerufen hat, und so weiter bis zum Anfang.
Im dritten Beispiel ruft ganzOben die Funktion inDerMitte auf, und die ruft ganzUnten. Geknallt hat es in ganzUnten, weil 42 kein toUpperCase kennt. Der eigentliche Fehler steckt aber in ganzOben, denn dort kommt die 42 her.
Das ist der Grund, warum man den Stacktrace liest statt nur die erste Zeile: Die oberste Zeile sagt dir, wo es passiert ist, die darunter sagen dir, warum.
In fremdem Code sind die obersten Zeilen oft Bibliotheken, die dich nichts angehen. Such dann die erste Zeile, in der deine eigene Datei steht. Dort fängst du an.
Der Klassiker: is not a function
Eine Meldung siehst du besonders oft, und sie hat fast immer dieselbe Ursache: Du hast dich im Namen einer Methode vertippt.
liste.pusch(1) ist kein ReferenceError, denn liste gibt es ja. JavaScript sieht nach, ob das Objekt eine Eigenschaft pusch hat, findet keine, und bekommt undefined zurück. Dann versucht es, dieses undefined aufzurufen, und das ist ein TypeError.
Deshalb steht in der Meldung nicht „pusch gibt es nicht”, sondern liste.pusch is not a function. Sie zeigt auf den Aufruf und nicht auf den Tippfehler. Wenn du sie siehst, lies den Methodennamen in der betroffenen Zeile Buchstabe für Buchstabe.
Warum ein Tippfehler erst beim Ausführen auffällt
Beim Laden prüft der Browser nur die Struktur, nicht die Bedeutung. Ob anzhal irgendwo existiert, kann er zu diesem Zeitpunkt gar nicht wissen: Der Name könnte in einer anderen Datei stehen, oder er entsteht erst zur Laufzeit.
Deshalb schlägt ein Tippfehler in einem Namen erst zu, wenn die Zeile wirklich an die Reihe kommt. Steht sie in einem Handler, der selten läuft, kann der Fehler wochenlang unentdeckt bleiben.
Genau das ist übrigens einer der Gründe, warum es TypeScript gibt. Aber das ist ein eigener Kurs.
Zum Mitnehmen
Die Art des Fehlers sagt dir, wo du suchen musst. SyntaxError heißt: die Datei ist ungültig. ReferenceError heißt: den Namen gibt es hier nicht. TypeError heißt: den Wert gibt es, aber er kann das nicht.
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 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.