Nach dieser Lektion finden Sie anhand des Stack Traces die Zeile, in der ein Programm gescheitert ist, setzen die passenden console-Aufrufe ein und gehen mit einem Debugger Schritt für Schritt durch den Code.
Sagen Sie es vorher, bevor Sie weiterlesen: Das Programm stürzt ab. Welche Funktionen in main.js listet der Stack Trace, von der ersten "at"-Zeile an?
function total(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
function checkout(cart) {
return total(cart.items);
}
checkout({item: []});
Üben · Aufgabe 3 von 7
Ergänzen Sie die console-Methode, die eine Meldung mit dem Stack an dieser Stelle ausgibt.
console.____("who calls pay?");
console.("who
Üben · Aufgabe 4 von 7
Ordnen Sie in node inspect jedem Befehl zu, was er tut.
Üben · Aufgabe 5 von 7
Der Fehler tritt nach einem await auf. Welche Funktionen in main.js listet der Stack Trace?
async function load() {
await null;
throw new Error("late");
}
async function main() {
await load();
}
await main();
Denksport · Aufgabe 6 von 7
Knobelaufgabe. Der Fehler wird in einer Funktion erzeugt und in einer anderen geworfen. Welche Funktionen in main.js listet der Trace?
function makeError(message) {
return new Error(message);
}
function save() {
throw makeError("disk full");
}
save();
Anwenden · Aufgabe 7 von 7
Kleine Aufgabe: Nehmen Sie ein Programm von Ihnen mit einem Fehler, oder schreiben Sie report(students), das den Durchschnitt der Punkte jedes Schülers ausgibt, aber s.scores liest, während die Daten points haben. Führen Sie es aus, finden Sie im Stack Trace den ersten Frame in Ihrer Datei und beheben Sie die Zeile. Ergänzen Sie ein console.assert, das das Problem früher gefunden hätte. Führen Sie es dann mit node inspect aus, setzen Sie in average einen Breakpoint und sehen Sie sich numbers mit repl an.
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Ein Stack Trace, aus dem Programm gelesen
checkout übergibt cart.items, aber im Warenkorb steht ein Tippfehler: item. total liest dann reduce von undefined. Das Programm fängt den Fehler und gibt die ersten vier Zeilen seines Stacks aus, wobei die vollständige URL dieser Datei durch main.js ersetzt ist: den Fehler, den Frame, in dem er auftrat, den Aufrufer und die oberste Ebene des Moduls.
main.js
function total(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
function checkout(cart) {
return total(cart.items);
}
try {
checkout({item: [{price: 3}]}); // a typo: item instead of items
} catch (error) {
// The first four lines of the stack, with this file shown as main.js:
const lines = error.stack.split("\n").slice(0, 4);
console.log(lines.join("\n").replaceAll(import.meta.url, "main.js"));
}
Ausführen mit
node main.js
Ausgabe
TypeError: Cannot read properties of undefined (reading 'reduce')
at total (main.js:2:16)
at checkout (main.js:6:10)
at main.js:10:3
Zeile 2, Spalte 16 ist der Punkt vor reduce: die Stelle, an der undefined gelesen wurde.
Der Fehler liegt in den Daten des Aufrufers, zwei Frames weiter unten: Lesen Sie weiter, bis Sie die Zeile erreichen, die den falschen Wert übergab.
error.stack ist ein gewöhnlicher String; seine genaue Form stammt von V8, parsen Sie ihn also nicht in Produktivcode.
Übungen
Übung 1 von 2
Dem Trace folgen
orderTotal addiert die Bestellzeilen; jede Zeile ist {quantity, product: {name, price: {amount}}}. Die Tests scheitern mit TypeError: Cannot read properties of undefined (reading 'amount'), und der erste Frame des Traces ist at priceOf (main.js:2:21). Sehen Sie sich diese Zeile und die Daten an und beheben Sie es.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
Hinweise
Hinweis 1
Zeile 2, Spalte 21 ist die Stelle, an der .amount gelesen wird. Welcher Wert davor ist undefined?
Hinweis 2
Vergleichen Sie line.price mit der Form einer Zeile in den Tests.
Hinweis 3
Der Preis steckt in product: line.product.price.amount.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
function priceOf(line) {
return line.product.price.amount * line.quantity;
}
export function orderTotal(order) {
return order.lines.reduce((sum, line) => sum + priceOf(line), 0);
}
Auf dem eigenen Computer ausführen
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
function priceOf(line) {
return line.price.amount * line.quantity;
}
export function orderTotal(order) {
return order.lines.reduce((sum, line) => sum + priceOf(line), 0);
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {orderTotal} from './main.js';
const order = {
lines: [
{quantity: 2, product: {name: 'Tea', price: {amount: 3}}},
{quantity: 1, product: {name: 'Mug', price: {amount: 8}}}
]
};
test('2 Tee zu 3 und 1 Tasse zu 8 ergeben 14', () => {
const total = orderTotal(order);
assert.equal(total, 14, `orderTotal lieferte ${total}`);
});
test('eine leere Bestellung ist 0', () => {
assert.equal(orderTotal({lines: []}), 0, 'eine leere Bestellung sollte 0 sein');
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.js
Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):
stackNames(error) soll die Funktionsnamen der "at"-Zeilen eines V8-Stack-Traces zurückgeben, die neueste zuerst: für "at total (main.js:2:16)" ist der Name total, "at async main (…)" ist main, und eine Zeile ohne Namen in Klammern, etwa "at main.js:10:3", wird weggelassen. Der Starter macht auch aus der ersten Zeile, der Fehlermeldung, einen Namen.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
Hinweise
Hinweis 1
Führen Sie die Tests aus: Wie sieht der erste Name aus?
Hinweis 2
Nur Zeilen, die mit "at " beginnen, sind Frames.
Hinweis 3
Ergänzen Sie im Filter line.startsWith("at ").
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {stackNames} from './main.js';
test('die Namen eines aufgeschriebenen Traces, neueste zuerst', () => {
const error = {stack: 'TypeError: x (y) is wrong\n at total (main.js:2:16)\n at async main (main.js:6:10)\n at main.js:10:3'};
const names = stackNames(error);
assert.deepEqual(names, ['total', 'main'], `stackNames lieferte ${JSON.stringify(names)}`);
});
test('ein echter Fehler beginnt mit der Funktion, die ihn erzeugt hat', () => {
function inner() {
return new Error('boom');
}
function outer() {
return inner();
}
const names = stackNames(outer());
assert.deepEqual(names.slice(0, 2), ['inner', 'outer'], `stackNames lieferte ${JSON.stringify(names)}`);
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.js
Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):
const names = ["ada", "linus"];
for (const name of names) {
console.log(name.toUppercase());
}
Was Node.js ausgibt
TypeError: name.toUppercase is not a function
Warum, und die Lösung
Die Meldung nennt das Problem bereits: name.toUppercase gibt es nicht, es ist also undefined, und undefined lässt sich nicht aufrufen. Suchen Sie zuerst nach einem Tippfehler: Die Methode heißt toUpperCase, mit großem C.
Ein Trace, der eine Funktion wiederholt
function countDown(n) {
console.log(n);
return countDown(n - 1);
}
countDown(3);
Was Node.js ausgibt
RangeError: Maximum call stack size exceeded
Warum, und die Lösung
Der Trace zeigt countDown immer wieder: Die Funktion ruft sich ohne Ende selbst auf. Jede rekursive Funktion braucht einen Fall, der aufhört: if (n === 0) return;. Ein Trace voller gleicher Frames weist immer darauf hin.
Eine Fehlermeldung ohne Kontext
function setAge(user, text) {
const age = Number(text);
if (Number.isNaN(age)) throw new Error("Invalid value");
user.age = age;
}
setAge({name: "Ada"}, "forty");
Was Node.js ausgibt
Error: Invalid value
Warum, und die Lösung
Der Trace zeigt, wo der Fehler geworfen wurde, aber die Meldung sagt nicht, welcher Wert falsch war und warum. Schreiben Sie die Fakten in die Meldung: throw new TypeError(`age must be a number, got "${text}"`). Wer das Log liest, vielleicht Sie selbst in sechs Monaten, kann es dann ohne Debugger beheben.
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.
Kernideen
Ein Stack Trace, von oben nach unten
Die erste Zeile eines Stack Traces ist der Fehler: sein name und seine message. Dann folgen die Frames, je eine "at"-Zeile, der neueste zuerst: at total (main.js:2:16) heißt, der Fehler trat in der Funktion total auf, Zeile 2, Spalte 16. Die nächsten Zeilen zeigen, wer sie aufgerufen hat, bis zur obersten Ebene des Moduls. Beginnen Sie beim ersten Frame in Ihren eigenen Dateien; Frames wie node:internal/… gehören zu Node.js. Das Format stammt von V8; andere Engines schreiben es anders.
console-Aufrufe zum Debuggen
console.trace("here") gibt Trace: here und den Stack an dieser Stelle aus, so sehen Sie, wer eine Funktion aufgerufen hat. console.assert(condition, message) gibt Assertion failed: message nur aus, wenn die Bedingung false ist, und das Programm läuft weiter. Beide schreiben wie console.error nach stderr, die eigentliche Ausgabe des Programms auf stdout bleibt also sauber. console.log({total, count}) gibt Namen mit ihren Werten aus. Entfernen Sie diese Aufrufe, sobald der Fehler gefunden ist.
Breakpoints
Ein Breakpoint hält das Programm in einer Zeile an, damit Sie jede Variable ansehen und Schritt für Schritt weitergehen können. Schreiben Sie debugger; in eine Zeile: Sie hält dort an, wenn ein Debugger angehängt ist, und tut sonst nichts. node inspect main.js startet den Terminal-Debugger von Node.js: cont läuft zum nächsten Breakpoint, next führt die nächste Zeile aus, step geht in einen Funktionsaufruf hinein, repl lässt Sie Ausdrücke eintippen. node --inspect-brk main.js wartet, bis Chrome DevTools oder Ihr Editor sich anhängt.