mitmario.dev

Debuggen

JavaScript Im Browser 5 Min Lesezeit 3 BeispieleLektion 4 von 6

Es gibt zwei Arten, einen Fehler zu suchen. Die eine besteht darin, Code zu ändern, bis es wieder geht. Die andere besteht darin, herauszufinden, wo es schiefgeht, und dann genau dort etwas zu ändern.

Die erste fühlt sich schneller an. Sie ist es nie, und sie hinterlässt Code, den niemand mehr versteht, weil an drei Stellen etwas steht, das gar nichts mit dem Problem zu tun hatte.

Diese Lektion ist die zweite Art.

Die Reihenfolge, die fast immer funktioniert

Vier Schritte, und meistens bist du nach dem zweiten fertig.

  1. Die Fehlermeldung lesen. Ganz. Art, Text, Datei, Zeile. Das klingt selbstverständlich und wird trotzdem übersprungen, weil rote Schrift unangenehm ist.
  2. Die genannte Zeile ansehen. Nicht die Zeile, an der du zuletzt gearbeitet hast, sondern die, die in der Meldung steht.
  3. Die Werte prüfen. Was steht wirklich in den Variablen, die dort vorkommen? Nicht was stehen sollte.
  4. Halbieren. Wenn du immer noch nicht weißt, wo es klemmt, teile den Weg in der Mitte und sieh nach, ob der Zwischenstand dort noch stimmt.

Bei einem Fehler ohne Meldung, also wenn einfach das Falsche herauskommt, fängst du direkt bei Schritt 3 an.

console.log richtig benutzen

Beschriftete Ausgaben statt nackter Werte
const warenkorb = [
  { name: "Tastatur", preis: 4900, menge: 2 },
  { name: "Maus", preis: 1900, menge: 1 },
  { name: "Kabel", preis: 700, menge: 3 },
];

// So nicht: drei Zahlen ohne Zusammenhang.
console.log(warenkorb.length);
console.log(warenkorb[0].preis);
console.log(warenkorb[0].preis * warenkorb[0].menge);

// Besser: jede Ausgabe sagt, was sie zeigt.
console.log("Positionen:", warenkorb.length);
console.log("Preis der ersten Position:", warenkorb[0].preis);
console.log("Zeilensumme der ersten Position:", warenkorb[0].preis * warenkorb[0].menge);

// Für eine Liste von Objekten: console.table.
console.table(warenkorb);

console.log ist das Werkzeug, mit dem du dem Programm beim Denken zusiehst. Es hat nur eine Schwäche, und die ist selbstgemacht: Wer fünf nackte Werte ausgibt, sieht in der Console fünf Zahlen und weiß nicht mehr, welche wozu gehört.

Schreib deshalb immer eine Beschriftung davor. Ein Aufruf nimmt beliebig viele Argumente und setzt sie durch Leerzeichen getrennt hintereinander.

Für eine Liste von Objekten gibt es console.table. Sie zeigt eine echte Tabelle mit einer Spalte je Eigenschaft und ist bei Daten aus dem Netz Gold wert.

Für Schleifen gibt es console.group und console.groupEnd. Alles dazwischen wird eingerückt und zusammenklappbar.

Eine Schleife, die sich selbst erklärt
const preise = [1290, 450, 9900];
let summe = 0;

for (let i = 0; i < preise.length; i++) {
  console.group(`Durchlauf ${i}`);
  console.log("Preis:", preise[i]);
  console.log("Summe vorher:", summe);
  summe = summe + preise[i];
  console.log("Summe nachher:", summe);
  console.groupEnd();
}

console.log("Endsumme:", summe);

Der Unterschied wird bei zwanzig Durchläufen deutlich: Ohne Gruppen ist die Console eine Wand aus Zeilen, mit Gruppen ist sie eine Liste, die du aufklappen kannst.

Beides siehst du auch in der Console dieses Kurses: die Tabelle als Tabelle, die Gruppen zum Auf- und Zuklappen. Ein Objekt kannst du dort genauso aufklappen wie in den DevTools. Und wenn ein Fehler passiert, steht in der Zeile, aus welcher Datei und Zeile er kam. Ein Klick darauf bringt dich im Editor genau dorthin.

Die DevTools

Irgendwann reicht console.log nicht mehr, und zwar genau dann, wenn du nicht weißt, wonach du suchst. Eine Ausgabe zeigt dir immer nur, was du vorher ausgewählt hast. Ein Haltepunkt zeigt dir alles.

So kommst du hin: F12 drücken oder rechtsklicken und „Untersuchen” wählen. Im Reiter Sources (in Firefox: Debugger) findest du links deine Dateien.

Klick in einer Datei auf die Zeilennummer, an der du anhalten willst. Die Zeile wird markiert, das ist der Haltepunkt. Lad die Seite neu oder löse die Stelle aus. Sobald die Zeile an die Reihe kommt, hält das Programm an, und zwar mitten drin.

Jetzt siehst du rechts im Bereich Scope jede Variable, die an dieser Stelle gilt, mit ihrem echten Wert. Darunter steht der Call Stack, also derselbe Weg wie im Stacktrace, nur lebendig.

Oben stehen die Knöpfe zum Weitergehen:

  • Weiter lässt das Programm bis zum nächsten Haltepunkt laufen.
  • Step over geht eine Zeile weiter und springt dabei über Funktionsaufrufe hinweg.
  • Step into geht in den Aufruf hinein.
  • Step out läuft die aktuelle Funktion zu Ende und hält beim Aufrufer.

Das Schlüsselwort debugger tut dasselbe wie ein Haltepunkt, nur aus dem Code heraus. Schreib debugger; in eine Zeile, und das Programm hält dort an, sobald die DevTools offen sind. Praktisch, wenn die Stelle schwer zu finden ist. Zum Aufräumen vor dem Speichern gehört es trotzdem.

Der Reiter Debug

Anhalten kannst du hier nicht. Was ein Haltepunkt dir aber eigentlich beantwortet, nämlich „was stand in diesem Moment in meinen Variablen”, das beantwortet der Reiter Debug, den du seit Lektion 4.1 kennst.

Dort war er das Mittel, einer Schleife beim Zählen zuzusehen. Hier ist er das Werkzeug beim Fehlersuchen, und das ist derselbe Handgriff mit einer anderen Absicht: Statt console.log-Zeilen einzubauen, drückst du auf Aufzeichnen und blätterst durch die Liste.

Genau das brauchst du in der Aufgabe zu dieser Lektion. Du siehst in der ersten Schleife, dass i bei 1 anfängt und nicht bei 0, ohne eine einzige Zeile in den Code zu schreiben, die du hinterher wieder herausnehmen müsstest.

Drei Grenzen, und die dritte ist die unangenehme. Aufgezeichnet wird die Datei, an der du arbeitest. Der Schalter tut nichts, solange dein Code einen Syntaxfehler hat: Dann steht die Meldung in der Console, und die liest du zuerst. Und bei einer Schleife, die wirklich nie endet, hilft der Reiter dir nicht. Nachgemessen hängt dann die ganze Seite, auch der Reiter selbst, und daran ändert kein Werkzeug etwas. Was gedeckelt wird, ist die sehr lange, aber endliche Schleife: Nach zweitausend Schritten hört die Aufzeichnung auf und sagt es.

Was der Reiter nicht kann, ist warten. Ein Haltepunkt hält das Programm an, und du kannst dich in Ruhe umsehen, bevor es weitergeht. Hier läuft alles durch, und du siehst hinterher nach. Für die meisten Fehler reicht das; für alles andere brauchst du die Entwicklerwerkzeuge, und Lektion 20.5 zeigt, wie du dort hinkommst.

Dein Code steht dort übrigens sehr wohl, nur nicht als app.js: Er landet in einem zusammengebauten Dokument, in dem deine Zeile 12 irgendwo bei Zeile 1600 steht. Ein debugger; in deinem Code hält damit wirklich an, sobald die Entwicklerwerkzeuge offen sind. Bequem ist es nicht.

Halbieren

Halbieren, bis es weh tut
function schritt1(text) {
  return text.trim();
}
function schritt2(text) {
  return text.split(",");
}
function schritt3(teile) {
  return teile.map((teil) => Number(teil));
}
function schritt4(zahlen) {
  return zahlen.reduce((a, b) => a + b, 0);
}

const eingabe = " 10, 20, dreißig ";

// Nicht raten, sondern nachsehen. Eine Zeile je Zwischenstand.
const a = schritt1(eingabe);
console.log("nach schritt1:", a);
const b = schritt2(a);
console.log("nach schritt2:", b);
const c = schritt3(b);
console.log("nach schritt3:", c);
const d = schritt4(c);
console.log("nach schritt4:", d);

Der ehrlichste Rat dieses Kurses: Wenn du nicht weißt, wo der Fehler ist, halbiere den Code, bis du es weißt.

Eine Kette aus vier Schritten liefert am Ende etwas Falsches. Statt alle vier durchzulesen, sieh dir den Zwischenstand nach Schritt 2 an. Stimmt er, liegt das Problem in der zweiten Hälfte. Stimmt er nicht, in der ersten. Danach halbierst du wieder.

Mit drei oder vier Halbierungen findest du die Stelle in einer Kette aus zwanzig Schritten. Durch Lesen findest du sie vielleicht auch, aber du weißt hinterher nie, ob du wirklich alles gesehen hast.

Im dritten Beispiel steht dreißig in den Daten, und Number("dreißig") ist NaN. Die Ausgabe nach Schritt 3 zeigt es sofort. Ohne die Zwischenstände siehst du nur, dass am Ende NaN herauskommt, und suchst genauso gut in reduce.

Der letzte Trick

Wenn gar nichts mehr hilft: Erklär das Problem laut. Einer Kollegin, einem Kuscheltier, dem leeren Raum. Die Hälfte aller Fehler löst sich beim Formulieren des zweiten Satzes.

Das ist kein Scherz, sondern ein bekanntes Verfahren mit eigenem Namen. Es funktioniert, weil du beim Erklären Annahmen aussprichst, die du beim Lesen überspringst.

Zum Mitnehmen

Wenn du nicht weißt, wo der Fehler ist, halbiere den Code, bis du es weißt. Raten ist der langsamste Weg, auch wenn es sich schneller anfühlt.

Jetzt du

Basis Konto, kostenlos

Zu 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.