Exception Handling  

Chapter 18: Advanced Error Handling, Debugging, and Defensive Code

Professional JavaScript development involves not just writing code that works, but writing code that is robust and predictable. This means learning how to anticipate, catch, and gracefully handle errors, and how to use the browser's tools to diagnose problems.

1. The try...catch...finally Block

This structure is the cornerstone of synchronous error handling in JavaScript (and asynchronous error handling using async/await as covered in Chapter 13).

  • try: Contains the code that you want to monitor for potential errors.

  • catch: Contains code that runs only if an error is thrown in the try block. It receives the error object as an argument.

  • finally: Contains code that runs always, regardless of whether an error was thrown or caught (often used for cleanup).

JavaScript

function divide(a, b) {
    try {
        if (b === 0) {
            // Manually throw an error for a specific condition
            throw new Error("Division by zero is not allowed.");
        }
        return a / b;
    } catch (error) {
        // Handle the error gracefully
        console.error("An error occurred:", error.message);
        // Return a safe default value
        return null;
    } finally {
        console.log("Division attempt finished.");
    }
}

console.log(divide(10, 2)); // Output: Division attempt finished. | 5console.log(divide(10, 0)); // Output: An error occurred: Division by zero is not allowed. | Division attempt finished. | null

2. Custom Errors

Instead of throwing generic Error objects, you can create your own custom error classes that inherit from the built-in Error class. This allows you to differentiate between types of errors (e.g., ValidationError, NetworkError).

JavaScript

class NetworkError extends Error {
    constructor(message) {
        super(message);
        this.name = 'NetworkError';
    }
}

try {
    throw new NetworkError('Failed to connect to API server.');
} catch (error) {
    if (error.name === 'NetworkError') {
        console.warn('Network issue detected. Showing offline message.');
    } else {
        console.error('Unhandled critical error:', error);
    }
}

3. Defensive Programming and Type Checking

Defensive programming involves writing code that anticipates bad data or unexpected input. In loosely-typed JavaScript, checking the type and existence of variables is essential before operating on them.

  • Optional Chaining (?.): Safely access nested properties without throwing an error if an intermediate property is null or undefined.

    JavaScript

    const user = { profile: { name: 'Alex' } };
    // Before: user.address && user.address.cityconst city = user.address?.city; // city is undefined, no error thrown
  • Nullish Coalescing (??): Provides a default value only if the value is strictly null or undefined. (It treats 0 or '' as valid values, unlike the || operator).

    JavaScript

    const providedLimit = 0;
    const limit = providedLimit ?? 100; // limit is 0 (0 is considered a valid value)
    
    const providedName = null;
    const name = providedName ?? 'Guest'; // name is 'Guest'

4. Browser DevTools for Debugging

The browser's Developer Tools (F12 or Ctrl+Shift+I) are your primary tools for debugging.

DevTools TabUsage
ConsoleViewing console.log() output, errors, and warnings. You can interactively run JavaScript code here.
SourcesThe Debugger. You can view your source code and set Breakpoints—lines of code where execution pauses so you can inspect variables and step through code line-by-line.
NetworkMonitoring all fetch or AJAX requests, viewing their headers, and checking response data and timing.
ElementsInspecting the live DOM tree and how JavaScript is manipulating it.

Export to Sheets

Key Debugger Actions

  • Set a Breakpoint: Click the line number in the Sources tab.

  • Step Over: Execute the current line and move to the next.

  • Step Into: Jump inside a function call on the current line.

  • Watch: Monitor the value of specific variables as the code runs.