Master JavaScript error handling, debugging techniques, and best practices for writing robust code.
Error handling is a critical skill in JavaScript development that separates professional developers from beginners. JavaScript errors are inevitable in programming, but understanding how to handle them gracefully and debug them effectively helps you write more robust applications and maintain better user experiences. This lesson covers different types of JavaScript errors, structured error handling with try-catch-finally blocks, debugging techniques using browser developer tools, and best practices for error prevention and logging.
JavaScript errors are inevitable in programming, but understanding them helps you write more robust applications and debug issues effectively. There are four main types of JavaScript errors, each with distinct characteristics and causes.
Syntax Errors occur when the code structure violates JavaScript grammar rules, preventing the script from executing at all:
1// Syntax Error - Missing closing bracket
2function broken() {
3 console.log("Hello World"
4} // Missing )
5
6// Syntax Error - Invalid variable name
7const 123invalid = "error";
8
9// Syntax Error - Missing quotes
10console.log(Hello World);Reference Errors happen when trying to access variables or functions that don't exist or aren't in scope:
1// ReferenceError - Variable doesn't exist
2console.log(undefinedVariable);
3
4// ReferenceError - Function doesn't exist
5nonexistentFunction();
6
7// ReferenceError - Accessing before declaration
8console.log(myVar);
9let myVar = "Hello";Type Errors occur when performing operations on inappropriate data types:
1// TypeError - Cannot read property of null/undefined
2let obj = null;
3console.log(obj.property); // TypeError
4
5// TypeError - Function is not defined
6let notAFunction = "hello";
7notAFunction(); // TypeError
8
9// TypeError - Cannot convert to object
10let num = 42;
11num.toUpperCase(); // TypeErrorRange Errors are thrown when values are outside the acceptable range:
1// RangeError - Invalid array length
2let arr = new Array(-1); // RangeError
3
4// RangeError - Number out of range
5let num = 999;
6num.toPrecision(101); // RangeError
7
8// RangeError - Stack overflow (infinite recursion)
9function infiniteRecursion() {
10 infiniteRecursion();
11}
12infiniteRecursion(); // RangeErrorJavaScript provides structured error handling through try-catch-finally blocks, allowing you to gracefully handle errors and maintain application flow. This approach prevents crashes and provides better user experiences.
Basic Try-Catch Structure:
1// Basic try-catch
2try {
3 // Code that might throw an error
4 let result = riskyOperation();
5 console.log("Success:", result);
6} catch (error) {
7 // Handle the error
8 console.error("An error occurred:", error.message);
9}
10
11// Real example - Parsing JSON
12function parseJSON(jsonString) {
13 try {
14 const data = JSON.parse(jsonString);
15 return { success: true, data: data };
16 } catch (error) {
17 return {
18 success: false,
19 error: "Invalid JSON: " + error.message
20 };
21 }
22}
23
24console.log(parseJSON('{"name": "John"}')); // Success
25console.log(parseJSON('invalid json')); // Error handledTry-Catch-Finally Pattern:
1function processData(data) {
2 console.log("Starting data processing...");
3
4 try {
5 // Attempt to process data
6 if (!data) {
7 throw new Error("No data provided");
8 }
9
10 let processed = data.toUpperCase();
11 console.log("Processed:", processed);
12 return processed;
13
14 } catch (error) {
15 // Handle any errors
16 console.error("Processing failed:", error.message);
17 return null;
18
19 } finally {
20 // Always runs, regardless of success or failure
21 console.log("Data processing completed");
22 }
23}
24
25// Test the function
26processData("hello world"); // Success case
27processData(null); // Error caseCustom errors help create more meaningful error messages and better error handling in your applications. They provide specific context and make debugging much easier.
Throwing Custom Errors:
1// Throwing simple string errors
2function validateAge(age) {
3 if (age < 0) {
4 throw "Age cannot be negative";
5 }
6 if (age > 150) {
7 throw "Age seems unrealistic";
8 }
9 return age;
10}
11
12// Throwing Error objects (better practice)
13function validateEmail(email) {
14 if (!email) {
15 throw new Error("Email is required");
16 }
17 if (!email.includes('@')) {
18 throw new Error("Email must contain @ symbol");
19 }
20 if (email.length < 5) {
21 throw new Error("Email too short");
22 }
23 return email;
24}
25
26// Using custom errors
27try {
28 let age = validateAge(-5);
29} catch (error) {
30 console.log("Age error:", error);
31}
32
33try {
34 let email = validateEmail("bad");
35} catch (error) {
36 console.log("Email error:", error.message);
37}Custom Error Classes:
1// Create custom error types
2class ValidationError extends Error {
3 constructor(message, field) {
4 super(message);
5 this.name = "ValidationError";
6 this.field = field;
7 }
8}
9
10class NetworkError extends Error {
11 constructor(message, statusCode) {
12 super(message);
13 this.name = "NetworkError";
14 this.statusCode = statusCode;
15 }
16}
17
18// Using custom error classes
19function validateUser(userData) {
20 if (!userData.name) {
21 throw new ValidationError("Name is required", "name");
22 }
23 if (!userData.email) {
24 throw new ValidationError("Email is required", "email");
25 }
26 if (userData.age < 18) {
27 throw new ValidationError("Must be at least 18 years old", "age");
28 }
29}
30
31// Handle custom errors
32try {
33 validateUser({ name: "John" }); // Missing email
34} catch (error) {
35 if (error instanceof ValidationError) {
36 console.log(`Validation failed for ${error.field}: ${error.message}`);
37 }
38}Effective debugging is crucial for identifying and fixing issues in your JavaScript code. Modern browsers provide powerful debugging tools, and JavaScript offers various debugging techniques.
Console Debugging Methods:
1// Basic console methods
2console.log("Basic message");
3console.info("Information message");
4console.warn("Warning message");
5console.error("Error message");
6
7// Enhanced logging with objects
8const user = { name: "John", age: 30, city: "New York" };
9console.log("User object:", user);
10console.table(user); // Display as table
11
12// Group related logs
13console.group("User Processing");
14console.log("Processing user:", user.name);
15console.log("User age:", user.age);
16console.groupEnd();
17
18// Timing operations
19console.time("Operation Duration");
20setTimeout(() => {
21 console.timeEnd("Operation Duration");
22}, 1000);
23
24// Stack trace
25function functionA() {
26 functionB();
27}
28function functionB() {
29 functionC();
30}
31function functionC() {
32 console.trace("Execution path");
33}
34functionA();Debugging with Breakpoints:
1// Using debugger statement
2function complexCalculation(a, b, c) {
3 let step1 = a * b;
4 debugger; // Execution will pause here in browser dev tools
5
6 let step2 = step1 + c;
7 debugger; // Another breakpoint
8
9 let result = Math.sqrt(step2);
10 return result;
11}
12
13// Conditional debugging
14function processArray(arr) {
15 for (let i = 0; i < arr.length; i++) {
16 if (i === 3) {
17 debugger; // Only pause on 4th iteration
18 }
19 console.log(`Processing item ${i}: ${arr[i]}`);
20 }
21}Learn systematic approaches to debugging and best practices for preventing and handling errors in production code. A methodical approach to debugging saves time and prevents recurring issues.
Systematic Debugging Approach:
Defensive Programming Practices:
1// Input validation
2function processUserData(userData) {
3 // Validate input exists
4 if (!userData) {
5 throw new Error("User data is required");
6 }
7
8 // Validate input type
9 if (typeof userData !== 'object') {
10 throw new Error("User data must be an object");
11 }
12
13 // Validate required properties
14 const requiredFields = ['name', 'email', 'age'];
15 for (const field of requiredFields) {
16 if (!userData.hasOwnProperty(field)) {
17 throw new Error(`Missing required field: ${field}`);
18 }
19 }
20
21 return {
22 name: userData.name.trim(),
23 email: userData.email.toLowerCase(),
24 age: userData.age
25 };
26}
27
28// Safe property access
29function getNestedProperty(obj, path, defaultValue = null) {
30 try {
31 return path.split('.').reduce((current, key) => {
32 return current && current[key] !== undefined ? current[key] : defaultValue;
33 }, obj);
34 } catch (error) {
35 return defaultValue;
36 }
37}Error Prevention Best Practices:
Code Organization:
Testing and Validation:
Error Handling:
Your progress is saved in this browser, on this device only.