JavaScript debugging beyond console.log
console.log works until the bug is three calls deep. Breakpoints, conditional breakpoints, logpoints and the call stack show what the code does while it runs.
console.log is the first debugging tool everyone learns, and for a quick check it is fine. It gets slow when the bug is hard: you add a log, reload, read, add another, reload again. A debugger answers the same questions in one run, and it shows values you did not think to print.
Everything below is in the Sources panel of Chrome DevTools. Firefox and Edge have the same features.
Pause the code with a breakpoint
Open the file in Sources and click a line number. The next time that line runs, execution stops there. You can also write debugger; in your code to get the same effect.
While paused you can:
| Action | Shortcut | What it does |
|---|---|---|
| Resume | F8 |
Run to the next breakpoint |
| Step over | F10 |
Run the current line and stop at the next one |
| Step into | F11 |
Go inside the function being called |
| Step out | Shift + F11 |
Finish the current function and return to the caller |
Hover over any variable to see its value. The Scope pane lists everything in reach at that moment.
Stop only when it matters
A breakpoint inside a loop that runs five hundred times is useless if the bug is on item 317. Right click the line number and choose Add conditional breakpoint:
item.price < 0
The code now pauses only when the condition is true.
Log without editing the code
A logpoint prints a message when a line runs, without pausing and without touching the source. Right click a line number, choose Add logpoint and enter:
"total after discount", total, discount
No stray console.log ends up in a commit.
Read the call stack
The Call Stack pane shows how the code arrived at the current line: which function called which. Click any frame to see the variables at that level. This is often the fastest way to learn why a function received a wrong value. The bug is usually one or two frames up.
Let the browser find the line for you
- Pause on exceptions stops at the exact line that throws.
- XHR/fetch breakpoints pause when a request URL contains a given text.
- DOM breakpoints pause when an element is changed or removed. Right click the element in the Elements panel and pick Break on.
- Event listener breakpoints pause on any click, key press or other event.
These help most when you do not know where the relevant code lives.
Better console calls
When logging is the right tool, use more of it:
console.table(cart.items); // rows and columns instead of nested objects
console.trace("price changed"); // prints the call stack
console.time("filter");
runFilter();
console.timeEnd("filter"); // how long it took
An example
A cart total is wrong after adding an item. Set a breakpoint in the function that updates the cart, add the item and step through. The Scope pane shows the quantity arriving as the string "2" instead of the number 2, so the sum becomes text joined together. The cause is found in one run.
Node.js works the same way. Start the program with node --inspect-brk app.js and attach DevTools or your editor.