Menu

JavaScript Console & DevTools: Debuggen ohne console.log

Die Console-Methoden und DevTools-Features, mit denen du JavaScript schneller debuggst, als überall console.log zu verteilen.

Die Konsole kann mehr als nur log

console.log ist das erste Debugging-Werkzeug, das die meisten in JavaScript lernen – und für viele bleibt es bis zum Schluss das einzige. Funktioniert ja auch. Trotzdem: Das console-Objekt bringt ein gutes Dutzend weiterer Methoden mit, die dich beim Debuggen schneller und klarer ans Ziel bringen. Und sobald du dich in den Chrome DevTools wirklich zu Hause fühlst – mit Breakpoints, Call Stack und Watch Expressions – wirst du automatisch seltener zu log greifen.

Verschaffen wir uns zunächst einen kurzen Überblick:

index.js
Output
Click Run to see the output here.

Alle vier schreiben in dieselbe Konsole, aber die Browser stellen sie unterschiedlich dar: Warnungen bekommen einen gelben Hintergrund, Fehler werden rot hervorgehoben und mit einem Icon versehen, und beide liefern einen Stacktrace mit. Über die Filter in den DevTools lassen sich die einzelnen Level ausblenden oder isoliert anzeigen – das wird spätestens dann wichtig, wenn deine App hunderte Meldungen pro Minute produziert.

Mehrere Werte ausgeben – der bequeme Weg

Wenn du mehrere Variablen gleichzeitig sehen willst, bau sie bloß nicht per String-Konkatenation zusammen. Übergib sie stattdessen als einzelne Argumente – oder noch besser: pack sie in ein Objekt, dann ist jeder Wert automatisch beschriftet:

index.js
Output
Click Run to see the output here.

Der Trick mit { user, count } nutzt die Object-Shorthand-Syntax: Der Variablenname wird automatisch zum Key. In der Konsole siehst du dann { user: {...}, count: 3 } und kannst user aufklappen, um reinzuschauen. Im Gegensatz zum Stringifyen bleibt die Struktur des Objekts dabei vollständig erhalten.

console.table für Arrays von Objekten

Wenn du ein Array von Objekten vor dir hast, spuckt console.log nur ein zusammengeklapptes Chaos aus. console.table macht daraus eine echte Tabelle:

index.js
Output
Click Run to see the output here.

In der Browser-Konsole zeigt der erste Aufruf alle drei Zeilen mit sortierbaren Spalten an. Der zweite Aufruf beschränkt die Ausgabe auf name und role. Für alles, was datenförmig ist – API-Antworten, Query-Ergebnisse, eingelesene CSV-Dateien – ist das ein riesiger Komfortgewinn.

console.dir vs. console.log

Auf den ersten Blick wirken beide identisch, bei DOM-Elementen verhalten sie sich aber unterschiedlich:

const el = document.querySelector("button");

console.log(el);   // gibt das HTML aus: <button>Click me</button>
console.dir(el);   // gibt die JS-Objektansicht mit allen Eigenschaften aus

log stellt Elemente als HTML dar. dir hingegen zeigt dir das reine Objekt – mit allen Eigenschaften, Event-Handlern und Verweisen auf berechnete Styles. Wenn du wissen willst, welche Methoden oder Attribute ein Element tatsächlich hat, ist dir dein Freund.

Zusammengehörige Logs gruppieren

Bei längeren Debug-Sessions geht in der Konsole schnell die Übersicht verloren. Mit console.group und console.groupEnd packst du zusammengehörige Ausgaben in einen einklappbaren Block:

index.js
Output
Click Run to see the output here.

Jeder Aufruf erzeugt eine benannte Gruppe, die du einklappen kannst. Wenn du möchtest, dass Gruppen standardmäßig zugeklappt starten, nimm console.groupCollapsed – praktisch, wenn du nur die verdächtigen Einträge aufklappen willst.

Performance messen mit console.time

Für schnelle Performance-Checks sind console.time und console.timeEnd kaum zu schlagen:

index.js
Output
Click Run to see the output here.

Das Label in time und timeEnd muss übereinstimmen. Du kannst auch mehrere Timer mit verschiedenen Labels parallel laufen lassen. Für alles, was über "ist diese Schleife lahm?" hinausgeht, wechselst du besser ins Performance-Panel der DevTools – dort bekommst du eine komplette Timeline samt Flame-Chart.

Assertions: nur loggen, wenn wirklich was schiefläuft

console.assert gibt nur dann etwas aus, wenn die Bedingung falsy ist. So kannst du Sanity-Checks dezent im Code lassen, ohne die Konsole vollzuspammen, solange alles passt:

index.js
Output
Click Run to see the output here.

Gut für Invarianten, die immer gelten sollten. Schlecht dagegen für Dinge, die legitim fehlschlagen können — dafür gehört ein echter Error geworfen.

Stacktrace auf Zuruf

console.trace gibt den aktuellen Call Stack aus, ohne etwas zu werfen. Praktisch, wenn du rausfinden willst, wer eine Funktion überhaupt aufgerufen hat:

index.js
Output
Click Run to see the output here.

Die Ausgabe lautet inner → outer → (top level). In einer echten Anwendung findest du so heraus, dass der Click-Handler, den du debuggst, tatsächlich von drei verschiedenen Stellen aus ausgelöst wird.

Das debugger-Statement

Am schnellsten pausierst du die JavaScript-Ausführung mit einem einzigen Wort:

function computeTotal(items) {
    const subtotal = items.reduce((s, i) => s + i.price, 0);
    debugger;
    return subtotal * 1.08;
}

Wenn die DevTools geöffnet sind, wirkt debugger; wie ein echter Breakpoint — die Ausführung hält genau an dieser Zeile an, und du bekommst den vollen Debugger: alle Variablen im aktuellen Scope, den Call Stack, Step-Over- und Step-Into-Buttons und die Möglichkeit, beliebige Ausdrücke im aktuellen Zustand auszuwerten. Sind die DevTools geschlossen, passiert bei debugger; schlicht nichts.

Das erste Mal einen richtigen Debugger statt console.log zu benutzen fühlt sich an wie ein Cheatcode. Du siehst jede Variable im Scope, ohne vorher zu entscheiden, was du ausgeben willst. Du kannst Schritt für Schritt durch Verzweigungen gehen und genau sehen, welcher Zweig ausgeführt wird. Kniffelige Bugs zu fixen dauert plötzlich Sekunden statt Minuten.

Die debugger-Zeile solltest du vor dem Commit wieder rausnehmen — oder noch besser: setze Breakpoints direkt in den DevTools, indem du im Sources-Panel auf eine Zeilennummer klickst.

DevTools-Tricks, die du kennen solltest

Ein paar Features, die viele jahrelang übersehen:

  • Conditional Breakpoints: Im Sources-Panel mit Rechtsklick auf eine Zeilennummer eine Bedingung wie user.id === 42 setzen. Der Breakpoint greift nur, wenn die Bedingung erfüllt ist.
  • Logpoints: dasselbe Menü, "Add logpoint." Schreibt eine Meldung in die Konsole, ohne zu pausieren und ohne dass du deinen Code anfassen musst.
  • $_ in der Konsole: das Ergebnis des zuletzt ausgewerteten Ausdrucks. Etwas ausführen, dann mit $_ wieder draufzugreifen.
  • $0: das aktuell im Elements-Panel markierte Element. $0.textContent zeigt dir den Textinhalt von dem, was du angeklickt hast.
  • Copy as object: Rechtsklick auf einen beliebigen Wert in der Konsole und "Store as global variable." auswählen. Du bekommst dann temp1, temp2 usw. zum Rumprobieren.
  • Network-Panel → Copy as fetch: macht aus jedem Request einen fetch()-Aufruf, den du in die Konsole einfügen und anpassen kannst.

Nichts davon ist lebensnotwendig. Aber sobald das alles in der Fingermuskulatur sitzt, sparst du jede Menge Zeit.

Aufräumen, bevor es in Produktion geht

Übrig gebliebene console.log-Aufrufe sind in der Entwicklung harmlos, in Produktion aber nur lästiges Rauschen. Ein paar Gewohnheiten, die helfen:

  • Eine Lint-Regel einsetzen (no-console in ESLint), um herumliegende Logs zu markieren — mit Ausnahmen für warn und error.
  • Ausführliches Logging in eine Abfrage packen: if (process.env.NODE_ENV !== "production") console.log(...).
  • Für Trace-Ausgaben lieber console.debug verwenden — die meisten Bundler und Log-Aggregatoren können das wegfiltern.
  • Noch besser: ein kleines Logger-Modul (oder eine Library wie debug) nutzen, damit sich ganze Log-Kategorien ein- und ausschalten lassen, ohne den Code anzufassen.

Logging ist nicht kostenlos. Jeder Aufruf serialisiert seine Argumente und schreibt in einen Buffer. In einer heißen Schleife kann ein vergessenes Log spürbar Performance kosten.

Als Nächstes: Reguläre Ausdrücke

Beim Debuggen geht es oft um Code, der Strings verarbeitet — und der steckt häufig voller Regex, dem kompaktesten und gleichzeitig kryptischsten Feature der Sprache. Im nächsten Kapitel gibt es eine entspannte Tour durch reguläre Ausdrücke in JavaScript und die Methoden, die sie nutzen.

Häufig gestellte Fragen

Was ist der Unterschied zwischen console.log und console.dir?

console.log gibt Werte in der Standarddarstellung des Browsers aus – bei DOM-Elementen heißt das: du siehst das gerenderte HTML. console.dir zeigt dagegen immer die JavaScript-Objektansicht mit allen Properties. Genau das willst du, wenn du nicht das Markup, sondern die Eigenschaften eines Elements inspizieren möchtest.

Wie debugge ich JavaScript in den Chrome DevTools ohne console.log?

Öffne das Sources-Panel, such deine Datei und klick auf eine Zeilennummer, um einen Breakpoint zu setzen. Sobald die Ausführung an dieser Stelle ankommt, pausiert sie – du kannst Variablen inspizieren, Schritt für Schritt durch den Code gehen und Ausdrücke direkt in der Console auswerten. Alternativ kannst du ein debugger;-Statement in den Code schreiben, um den Breakpoint aus dem Quellcode heraus auszulösen.

Wie messe ich, wie lange JavaScript-Code zum Ausführen braucht?

Pack den Code zwischen console.time('label') und console.timeEnd('label') – wichtig ist, dass beide Labels übereinstimmen. Die Console gibt dir dann die verstrichene Zeit in Millisekunden aus. Für detailliertere Analysen nimmst du das Performance-Panel in den DevTools und zeichnest einen Flame Chart von allem auf, was gelaufen ist.

Wofür ist console.table gut?

console.table rendert Arrays und Objekte als sortierbare Tabelle in der Console – deutlich übersichtlicher als ein verschachtelter Object-Dump. Besonders praktisch bei Arrays von Objekten: jedes Objekt wird zu einer Zeile, die Keys werden zu Spalten. Mit einem zweiten Argument kannst du außerdem einschränken, welche Spalten angezeigt werden.

Lerne mit Coddy zu programmieren

LOS GEHT'S