How to debug JavaScript code in a browser?

Lucjan Michałowski

Introduction

Debugging is an essential skill for every JavaScript developer. It involves the process of identifying and fixing errors, bugs, and unexpected behavior in your code. Effective debugging techniques not only save time but also improve code quality and enhance overall development productivity. In this article, I will explore some techniques to help you debug JavaScript code.

How to use console.log() effectively?

When it comes to debugging JavaScript code, console.log() is an invaluable tool in the developer’s arsenal. It allows you to output information to the browser’s console, providing insights into the state of your code and helping you identify and fix issues efficiently. The most basic use of console.log() is to output simple messages to the console. By placing console.log() statements strategically in your code, you can track the flow of execution and monitor the values of variables and objects:

console.log("Hello, world!");
console.log(variableName);

let price = 9.99;
console.log("Price:", price);

By logging the value of the price variable, you can easily verify its current state during execution. Note the lack of a space after Price:, it will be automatically added by the console.log().

console.log() supports the use of string formatting placeholders, allowing you to display dynamic values with additional context. You can include placeholders like %s for strings, %d for numbers, %o for objects:

let name = "John";
let age = 30;
console.log("Name: %s, Age: %d", name, age);

This will format the output by replacing %s with the value of name and %d with the value of age, providing a more descriptive log entry.

Alternatively, you can also use template strings:

let name = "John";
let age = 30;
console.log(`Name: ${name}, Age: ${age}`);

It is also useful in logging complex objects and arrays. It will output a representation of the object or array:

const employee = {
    firstName: "John",
    lastName: "Doe",
    address: {
        city: "London",
        country: "United Kingdom"
    }
};
console.log("Employee:", employee);

By logging the employee object, you can expand the output in the console to examine its properties and nested objects more easily.

Error levels

There are several methods available in the console object that allow you to log messages with different levels of severity. These methods include:

  • console.debug(): Outputs a debug-level message. It is typically used for informational purposes during development to provide additional details.
  • console.log(): Outputs a general log message. It is commonly used for general information and debugging purposes.
  • console.info(): Outputs an informational message. It is used to provide general information about the execution flow or state of the program.
  • console.warn(): Outputs a warning message. It indicates a potential issue or something that should be addressed but does not necessarily cause a program failure.
  • console.error(): Outputs an error message. It indicates that an error has occurred in the program and may cause the program to terminate or not function as expected.

By using different levels, it becomes easier to filter and prioritize messages based on their importance during debugging and troubleshooting processes.

Display objects in a table

To display an object in a table format in the browser console, you can use the console.table() method. This method takes an array or an object as input and displays it as a table in the console.

Here’s an example of how you can use console.table() to display an object in a table format:

const employee = {
    firstName: 'John',
    lastName: "Doe",
    position: 'Frontend Developer'
};
console.table(employee);

When you run this code in the browser console, it will display employee as a table, with columns for the property names (firstName, lastName, position) and corresponding values.

If you have an array of objects, you can also use console.table() to display it as a table:

const team = [
    { firstName: 'John', lastName: "Doe", position: 'Frontend Developer' },
    { firstName: 'Jane', lastName: "Doe", position: 'Graphic Designer' },
    { firstName: 'Bill', lastName: "Smith", position: 'Backend Developer' },
];
console.table(team);

This will display the team object as a table, with each object represented as a row in the table. The exact appearance of the table in the console may vary depending on the browser and console implementation.

Grouping console.log() entries

To group log entries using console.log(), you can use the console.group() and console.groupEnd() methods. These methods allow you to create labeled groups of log entries in the browser’s console, providing a structured and organized way to debug your code:

  • opening a log group:

To start a new group of log entries, use the console.group() method. You can pass a label or a description as an argument to specify the name of the group. For example:

console.group("Customer Details");

This will create a group labeled “Customer Details” in the console.

  • adding log entries within the group:

Within the log group, you can use console.log() to add individual log entries related to the specific group. For example:

console.log("Name: John Doe");
console.log("Age: 28");
console.log("Email: johndoe@example.com");

These log statements will appear within the “Customer Details” group in the console.

  • nesting log groups:

You can also nest log groups within other groups to create a hierarchical structure. This is useful when you want to organize and categorize your log entries based on different aspects of your code. To create a nested group, simply call console.group() within an existing group. For example:

console.group("Customer Details");
console.log("Name: John Doe");
console.log("Age: 30");

console.group("Address");
console.log("1 Main St");
console.log("London");
console.log("United Kingdom");
console.groupEnd();

console.groupEnd();

In this example, the “Address” group is nested within the “Customer Details” group, creating a hierarchical structure.

  • closing a log group:

To close a log group and indicate the end of a specific section, use the console.groupEnd() method. This will visually distinguish the end of a group in the console. Make sure to call console.groupEnd() for each nested group in the reverse order they were opened. For example:

console.groupEnd(); // Closes the "Address" group
console.groupEnd(); // Closes the "Customer Details" group

These calls will close the respective groups and complete the log grouping.

  • collapsing and expanding groups:

Most browser consoles allow you to collapse and expand log groups for better visibility and organization. You can click on the group label or the arrow icon next to it to collapse or expand the group’s contents. This is especially useful when dealing with large amounts of log entries.

By effectively using console.group() and console.groupEnd(), you can organize your log entries into logical groups, making it easier to navigate and analyze your debugging information. This approach helps improve the readability and clarity of your console output.

Measuring time in the console

To measure the execution time of a specific code block or function, you can use console.log(). These timers allow you to track the elapsed time between two points in your code, helping you optimize your application. Here’s how to do it:

  • starting a timer:

To start a timer, use the console.time() method, passing a unique label as an argument. This label will identify the specific timer. For example:

console.time("timer 1");

This will start a timer labeled “timer 1” and begin measuring the elapsed time.

  • executing the code:

Next, execute the code block or function that you want to measure the execution time for. This could be a specific section of your code or a function that performs a certain task.

  • stopping the timer:

To stop the timer and calculate the elapsed time, use the console.timeEnd() method, passing the same unique label used to start the timer. For example:

console.timeEnd("timer 1");

This will output the elapsed time in milliseconds to the console, with sub-millisecond precision, along with the label.

  • Multiple Timers:

You can use multiple timers simultaneously to measure the execution time of different sections or functions within your code. Just ensure that each timer has a unique label when starting and stopping.

By using console.log timers strategically, you can gather precise timing information and gain insights into the performance of your code and allow you to enhance the overall efficiency of your applications.

Printing the stack trace

You might come across a situation when you will need to provide information about the function calls that led to a certain point in the code. In most modern JavaScript environments, stack traces are captured by default. However, if you want to ensure that stack traces are available, make sure to disable any settings or optimizations that might remove or strip out stack trace information. You can place the console.trace() at the location where you want to log the stack trace.

function foo() {
    bar();
}

function bar() {
    console.trace();
}

foo();

Calling foo() will invoke bar(), which in turn logs the stack trace using console.trace(). The stack trace will show the function calls from the top-level foo() function down to the point where console.trace() is called.

Preserve logs

Remember that console messages are cleared on every page navigation, unless you check the Preserve log in the console settings.

Using the browser debugger.

The browser debugger is a powerful tool that is built into web browsers and enables developers to inspect, analyze, and debug web pages and web applications. It provides a comprehensive set of features and functionalities to assist in troubleshooting issues, understanding code execution, and optimizing performance.

The debugger keyword

The debugger keyword is a built-in feature in JavaScript that allows you to pause the execution of your code and start the debugging process. When the JavaScript interpreter encounters the debugger statement, it will automatically pause execution and open the browser’s debugger tools (if available) or trigger a breakpoint if a debugger is attached.

In order to use the debugger, you have to place the statement in your code where you want the program to pause:

function test() {
    // Some code here
    debugger; // Program pauses here for debugging
    // More code here
}

When the debugger statement is encountered during code execution, it will pause the program at that point. If a debugger is available and attached (e.g., the browser’s developer tools are open), the debugger will be launched, and you’ll have access to various debugging features. You can inspect variables, step through the code, set breakpoints, watch expressions, and more. The exact features and interface may vary slightly depending on the browser you are using.

After inspecting the program’s state and identifying any issues, you can choose to continue the execution of the code. The program will resume running until it either reaches the end or encounters another debugger statement.

It’s important to note that the debugger keyword is primarily used for interactive debugging during development. It’s advisable to remove or disable any debugger statements before deploying your code to production environments.

Setting breakpoints

You can set breakpoints for JavaScript code in the debugger window. When the program encounters a breakpoint, it suspends execution, allowing you to inspect variables, step through the code, and analyze its behavior at that point.

Setting breakpoints is similar to putting a debugger statement in the code. To set a breakpoint, you typically navigate to the “Sources” or “Debugger” tab of the browser’s developer tools. In the source code view, you can click on the line number where you want the execution to pause. Alternatively, you can right-click on the line and select “Add Breakpoint” from the context menu. Once set, a visual indicator like a red dot or a highlighted line is displayed to indicate the presence of the breakpoint.

Browser tools

Different browsers and debugging environments may have additional features and capabilities, so it’s beneficial to explore the specific debugger tools available in your preferred browser or integrated development environment (IDE).

Conclusion

Debugging JavaScript code is an essential skill that can greatly enhance your development process. By employing techniques like console.log, utilizing developer tools, setting breakpoints, and leveraging error messages, you can effectively identify and resolve issues in your JavaScript code. Remember to approach debugging with patience and attention to detail, as it is an iterative process.

Meet the geek-tastic people, and allow us to amaze you with what it's like to work with j‑labs!

Contact us