Running and Debugging JavaScript
console.log into a workflow: breakpoints, watchers,
profiles, and the network panel.
Three ways to run JavaScript
Console, Node, files
# 1. REPL — instant feedback for one expression or two
$ node
> 2 + 2
4
# 2. A file — how every demo in this track runs
$ node 23_runtime_lab.js
# 3. In the browser — the DevTools console (F12) has the page as its scope
> document.title
Same language in all three; the globals differ (lesson 17). Node demos print to your terminal; browser code prints to DevTools — always be certain which console you are reading.
The console is more than log
console.log(items); // objects collapse in the output…
console.table(items); // …arrays of objects render as a sortable grid
console.error("failed:", err); // red, and filterable as an error
console.assert(x > 0, "x went non-positive:", x); // logs ONLY when the condition is false
console.time("parse");
parse(bigJson);
console.timeEnd("parse"); // parse: 41.3ms — measure, do not guess
Breakpoints: pausing reality
The Sources-panel workflow
Open DevTools → Sources, click a line number to set a breakpoint, and trigger the code. Execution pauses and hands you three panels of superpowers:
- Scope — every local variable, right now. No logging required.
- Watch — pin expressions like
items.lengthand watch them change as you step. - Call Stack — the frames from lesson 15, clickable: jump to any caller.
Step controls: step over (next line), step into (enter the call), step out (finish this function). Ninety percent of debugging is: pause, read Scope, step over, compare against what you expected.
// The debugger; statement is a breakpoint in code —
// DevTools pauses when it runs (ignored while DevTools is closed):
function sumPrices(items) {
let total = 0;
for (const item of items) {
debugger; // pause here and inspect `item.price` — a string? that is the bug
total += item.price; // "4.50" + "12.00" concatenates: "04.5012.00"
}
return total;
}
This exact planted bug is demo runtime/26_devtools_lab.html — catch it with a
breakpoint, not by staring.
Source maps: debugging the shipped code
Production scripts are minified — thousands of characters on one line. A source map
maps each generated position back to the original source, so DevTools can show real names and
real lines. It ships as a //# sourceMappingURL=... comment; in DevTools settings
you choose whether production sources are mapped or left minified.
Performance: measuring, not guessing
The Performance panel records what the main thread actually did — scripting, style, layout, paint — as a flame chart: lesson 16's pipeline, made visible. The workflow: record, perform the slow action once, stop, and inspect the widest blocks. They are your target, not your suspicions.
// The same idea in code:
const start = performance.now(); // high-resolution timestamps
expensiveRender();
console.log(`render: ${(performance.now() - start).toFixed(1)} ms`);
// render: 12.4 ms — evidence beats intuition
Node gets the same DevTools
$ node --inspect 23_runtime_lab.js
# open chrome://inspect in a browser — breakpoints, scope, watch:
# the full Sources-panel experience for a server-side script
Practice: debug without console.log
The task
Open demo runtime/26_devtools_lab.html (Preview link on the Lab Examples page).
Click Run report and read the wrong total. Catch the bug using ONLY the Sources
panel: breakpoint, Scope, step over. Fix one line, re-run, confirm. Then click
Slow loop and read the timing from the console.
The checklist
- You reach for a breakpoint before reaching for console.log.
- You can say what the Scope, Watch, and Call Stack panes show.
- You know console.assert logs only on failure, and time/timeEnd measures a block.
- You can explain what a source map restores.