Running and Debugging JavaScript

You already run JavaScript constantly — in the console, in files, in Node. This lesson turns debugging from sprinkling 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.length and 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.