JavaScript Debugging

Debugging is the process of finding and fixing errors in your code. JavaScript provides several tools and techniques for debugging, from simple console methods to powerful browser DevTools.

console.log() and Friends

The console object has many methods beyond log(). Using the right method helps you organize and filter debugging output.

Console Methods for Debugging
// Basic logging
console.log('Simple message');
console.log('Value:', 42, 'Type:', typeof 42);

// Warnings (yellow in browser console)
console.warn('This is a warning');

// Errors (red in browser console)
console.error('This is an error message');

// Info
console.info('Informational message');

// String substitution
console.log('Hello %s, you are %d years old', 'Alice', 30);

// Styled output (browser only)
console.log('%cStyled text', 'color: blue; font-size: 20px');

console.table() and console.dir()

console.table() displays tabular data in a formatted table, making arrays and objects much easier to read. console.dir() displays an interactive list of object properties.

console.table() for Data Inspection
// Display array of objects as table
const users = [
  { name: 'Alice', age: 30, role: 'admin' },
  { name: 'Bob', age: 25, role: 'user' },
  { name: 'Charlie', age: 35, role: 'user' }
];
console.table(users);

// Display specific columns
console.table(users, ['name', 'role']);

// Display a single object
const config = { host: 'localhost', port: 3000, debug: true };
console.table(config);

// console.dir() for deep object inspection
const nested = { a: { b: { c: 42 } } };
console.dir(nested, { depth: null });

Grouping and Timing

Console groups help organize related log messages, and timing methods help measure code performance.

Console Grouping and Timing
// Grouping related messages
console.group('User Processing');
console.log('Loading users...');
console.log('Found 3 users');
console.log('Processing complete');
console.groupEnd();

// Collapsed group (starts closed in browser)
console.groupCollapsed('Details');
console.log('Detail 1');
console.log('Detail 2');
console.groupEnd();

// Timing code execution
console.time('array-creation');
const arr = Array.from({ length: 100000 }, (_, i) => i);
console.timeEnd('array-creation');
// array-creation: 5.123ms

// Count how many times something happens
for (let i = 0; i < 3; i++) {
  console.count('loop iteration');
}
// loop iteration: 1
// loop iteration: 2
// loop iteration: 3

The debugger Keyword

The debugger statement pauses code execution and opens the browser's debugging tools (if available). It acts like a programmatic breakpoint.

Using the debugger Keyword
function calculateTotal(items) {
  let total = 0;
  for (const item of items) {
    // Execution pauses here when DevTools is open
    debugger;
    total += item.price * item.quantity;
  }
  return total;
}

const items = [
  { name: 'Widget', price: 10, quantity: 3 },
  { name: 'Gadget', price: 25, quantity: 1 }
];

const total = calculateTotal(items);
console.log('Total:', total); // Total: 55
📝 Note: The debugger statement has no effect if no debugging tool is active. Always remove debugger statements from production code.

Browser DevTools and Breakpoints

Browser Developer Tools (F12 or Ctrl+Shift+I) provide the most powerful debugging experience. The Sources panel lets you set breakpoints, step through code, and inspect variables.

DevTools FeatureDescriptionShortcut
BreakpointPause execution at a specific lineClick line number in Sources
Conditional BreakpointPause only when condition is trueRight-click line number
Step Over (F10)Execute current line and move to nextF10
Step Into (F11)Jump into function callF11
Step Out (Shift+F11)Finish current function and returnShift+F11
Watch ExpressionsMonitor variable values as you stepAdd in Watch panel
Call StackSee the chain of function callsCall Stack panel
ConsoleExecute code in current scope while pausedConsole panel
Debugging Strategies in Practice
// Strategy 1: Isolate the problem
function processOrder(order) {
  console.log('Input order:', JSON.stringify(order));
  const subtotal = order.items.reduce((sum, item) => {
    const lineTotal = item.price * item.qty;
    console.log(`  ${item.name}: $${lineTotal}`);
    return sum + lineTotal;
  }, 0);
  console.log('Subtotal:', subtotal);
  return subtotal;
}

const order = {
  items: [
    { name: 'Book', price: 15, qty: 2 },
    { name: 'Pen', price: 3, qty: 5 }
  ]
};
console.log('Total:', processOrder(order));
//   Book: $30
//   Pen: $15
// Subtotal: 45
// Total: 45

// Strategy 2: Use assertions
function divide(a, b) {
  console.assert(b !== 0, 'Divisor should not be zero!');
  return a / b;
}
console.log(divide(10, 2)); // 5
divide(10, 0); // Assertion failed: Divisor should not be zero!
Exercise:
Which console method displays data in a formatted table?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.