"Translation" Official Guide Series: JavaScript Debugging Guide (Part 1)

✍🏼 Written on Sep 13, 2022    💡 Updated on Sep 13, 2022
❗️ Note: it has been days since this article was written, please be aware of its timeliness
🖥  Note:Official Guide Series Part 1
📚  Also published on Craft: https://www.craft.do/s/kyKcQKCEQjAksv

Through this comprehensive introduction to Chrome DevTools debugging features, we hope to elevate your debugging process to the next level.

First, you may need to learn basic debugging techniques here (English, pending translation).

Pausing Code with Breakpoints

By setting a breakpoint, you can pause code execution midway.

Check here (English, pending translation) to learn how to set a breakpoint.

Previewing Class/Function Properties on Hover

When code execution is paused, hovering over a class or function name allows you to preview its properties.

1

Step Through Code Execution

When your code is paused, you can execute it line by line to understand the call stack and related property values.

Step Over the Current Line of Code

When your breakpoint pauses on a line of code that contains a function but is unrelated to your current investigation, you can click Step Over to continue execution without entering that function.

step_over

Figure 1 👆🏻, the explanation of the meaning of Step Over in the blue box in the image above:

For example, suppose you are debugging the following code:

1
2
3
4
5
6
7
8
9
function updateHeader() {
var day = new Date().getDay();
var name = getName(); // A
updateName(name); // D
}
function getName() {
var name = app.first + ' ' + app.last; // B
return name; // C
}

At this point, your breakpoint is paused at position A. By clicking Step over, DevTools will execute all the code within the scope you “Step over”—in the example above, it steps over positions B and C inside the getName function at position A, causing DevTools to pause the code at position D.

Step Into the Current Line of Code

If the breakpoint pauses at a location relevant to the issue you’re investigating, and that location contains a function call, clicking Step into allows you to further inspect that function.

step_into

Figure 2 👆🏻, the explanation of the meaning of Step into in the blue box above:

For example, suppose you are debugging the following code:

1
2
3
4
5
6
7
8
9
function updateHeader() {
var day = new Date().getDay();
var name = getName(); // A
updateName(name);
}
function getName() {
var name = app.first + ' ' + app.last; // B
return name;
}

Your breakpoint is paused at position A. At this point, clicking Step into will execute this line of code and then pause at position B.

Step Out of the Current Line of Code

When the breakpoint pauses inside a function unrelated to the issue you’re investigating, you can click Step out to execute the remaining code in that function.

step_out

Figure 3 👆🏻, the explanation of the meaning of Step out in the blue box in the image above:

For example, suppose you are debugging the following code:

1
2
3
4
5
6
7
8
9
function updateHeader() {
var day = new Date().getDay();
var name = getName();
updateName(name); // C
}
function getName() {
var name = app.first + ' ' + app.last; // A
return name; // B
}

Your breakpoint is currently paused at position A. At this point, clicking Step out will cause DevTools to execute the remaining code in the getName() function, specifically the code at position B, and then pause the breakpoint at position C.

Continue Execution to a Specific Line

When debugging a long function, there may be many lines of code unrelated to the issue you’re investigating.

You could choose to step through these irrelevant lines one by one, but this process can be tedious. Alternatively, you could set a breakpoint on the desired line of code and press Resume Script Execution, but there’s an even faster method.

Right-click on the line where you want the breakpoint and select Continue to here. DevTools will resume execution until it reaches that line and then pause there.

continue_to_here

Figure 4 👆🏻, select Continue to here.

Resume Script Execution

You can click Resume Script Execution to continue running the code from where it was paused at the breakpoint. DevTools will execute the code until it encounters the next breakpoint (if any).

resume_script_execution

Figure 5 👆🏻, Resume Script Execution, highlighted in the blue box.

Forced Execution

You can long-press Resume Script Execution and select Force Script Execution to ignore all remaining breakpoints and execute the entire code directly.

force_script_execution

Figure 6 👆🏻, select Force Script Execution.

Changing the Current Thread’s Context

When debugging involves Web Worker or Service Worker, you can switch contexts by clicking on the items in the Threads panel’s context list. The blue arrow indicates the currently selected context.

change_threads_context

Figure 7 👆🏻, in the Threads panel, at the position marked by the blue box.

For example, imagine a scenario where your breakpoints exist simultaneously in your main script and your service worker script. You want to inspect local and global variables in the service worker, but the Source panel is currently displaying the context of your main script. In this case, you can click the service worker entry in the Threads panel to switch contexts and view the variables you’re interested in.

Viewing and Editing Variables/Properties in Local, Closure, and Global Scopes

When paused at a specific line, use the Scope panel to inspect and modify variables in local, closure, and global scopes.

  • Double-click a property value to edit it.

  • Non-enumerable properties are highlighted in gray.

查看和修改变量

Figure 8 👆🏻, in the Scope panel, the location marked by the blue box.

Viewing the Current Call Stack

When paused at a specific line, use the Call Stack panel to examine the sequence of function calls from the start of code execution up to your current breakpoint.

If your code contains asynchronous operations, you can check the Async checkbox to enable the asynchronous call stack.

Click on any entry to jump to the calling location of that function. The blue arrow icon indicates the currently highlighted function.

查看函数调用栈

Figure 9 👆🏻, in the Call Stack panel, the position marked by the blue box.

Note: If the code is not paused on a specific line, the Call Stack panel will be empty.

Re-executing Functions in the Call Stack

Sometimes you may want to observe the execution of a particular function without restarting the entire debugging process. When the breakpoint pauses inside the function, you can re-execute that function alone—in other words, you can reintroduce the function’s call context into the call stack.

Note: You can restart any function in the Call Stack, except for WebAssembly, async, and generator functions.

To re-execute a function:

  1. Use a breakpoint to pause the function. The Call Stack panel will record the sequence of function calls.

  2. In the Call Stack panel, right-click a function and select Restart frame from the context menu.

重新执行断点所在的函数

To understand how Restart frame executes, let’s assume the following code:

1
2
3
4
5
6
7
8
9
10
11
12
function foo(value) {
console.log(value);
bar(value);
}

function bar(value) {
value++;
console.log(value);
debugger;
}

foo(0);

The foo function takes 0 as a parameter, logs it to the console, and then calls the bar() function. Correspondingly, the bar function increments this value by 1.

Try executing these two functions from the beginning in the following way:

  • Copy the above code into a snippet, then run it. The breakpoint will stop at the line of code where debugger is located.

    ⚠️ Note: When code execution is paused, do not execute functions in the current call stack within the console, as this may cause unexpected errors.

  • You’ll notice that the current debugger displays the value next to the function declaration where it’s located: value = 1.

注意函数声明处右侧的值

  • Re-execute the bar() function.

重头执行bar

  • Press F9, then the code will execute past the line with the value increment, and break again at the debugger.

    Note that the value has changed to 2: value = 2.

重头执行debugger

  • Additionally, you can double-click the value in the Scope column to edit and set it to your desired value.

双击编辑值

  • Try executing the bar() function multiple times, and you’ll notice the value keeps increasing.

多次重复执行后值会一直增加

💡 Shocking! Why doesn’t value reset to 0?

When a function restarts execution, it does not reset its parameters. In other words, restarting does not restore the initial state when the function was first called. Therefore, it simply moves the current call pointer in the call stack back to the start of the function.

As a result, the current parameter value value persists in memory throughout repeated executions of the same function.

  • Now, restart the execution of the foo() function in the Call Stack.

重头执行foo函数

Look closely—the value has reverted back to 0.

value重新变为0

💡 Shocked again! Why was value reset to 0 this time?

It’s simple (paraphrased here): In JavaScript, parameters are passed by value. Since value is a primitive, modifying its value inside the function won’t affect its value outside the function.

Copying Call Stack Execution Path

Right-click anywhere in the Call Stack panel and select Copy Stack Trace to copy the current call stack to your clipboard.

复制调用栈

Image 10 👆🏻, select Copy Stack Trace.

The copied content will roughly look like this:

1
2
3
getNumber1 (get-started.js:35)
inputsAreEmpty (get-started.js:22)
onClick (get-started.js:15)

Ignoring a Specific Script or Scripts Meeting Certain Conditions

When debugging, you can ignore a script to skip over it. You would typically choose to ignore a script when its functions are complex and difficult to understand, and unrelated to your current debugging task.

For example, suppose you are debugging the following code:

1
2
3
4
5
function animate() {
prepare();
lib.doFancyStuff(); // A
render();
}

A is a third-party library you trust. If you’re absolutely certain that the issue you’re investigating has nothing to do with this third-party library, then ignoring it is a wise decision.

Ignoring a Script from the Source Editor Panel

  1. Open the file

  2. Right-click anywhere

  3. Select Add script to ignore list.

忽略脚本

Figure 11 👆🏻, ignoring a script from the editor panel.

Ignoring a Script from the Call Stack Panel

To ignore a script from the call stack, you need to:

  1. Right-click on a function within the call stack.

  2. Select Add script to ignore list.

从调用栈中忽略脚本

Figure 12 👆🏻, ignoring a script from the Call Stack.

Ignoring a Script from Settings

Translator’s Note: The settings referred to here are Devtools settings, not browser settings.

If you want to ignore a specific script or scripts matching certain conditions from the settings, you need to:

  1. Open Settings:

  2. Click the Ignore List tab.

  3. Click Add pattern.

  4. Enter the script name or a regular expression to match the script names you want to ignore.

  5. Click Add.

devoools设置界面

devtools忽略脚本

Figure 13 👆🏻, ignore scripts from Settings.

Run debug code Snippets from any page

If you find yourself repeatedly running certain debug code in the Console, consider using Snippets. Snippets are scripts you can store in DevTools and execute as needed.

Refer to https://developer.chrome.com/docs/devtools/javascript/snippets/ (untranslated) to learn more.

Translator’s note: Snippets execute with the current context. For example, if you pause during debugging and then run a Snippet, the code will have access to variables in the current context.

Watch the value of custom JavaScript expressions

Use the Watch panel to monitor the value of custom expressions. You can watch any valid JavaScript expression.

监听JavaScript表达式

Figure 14 👆🏻, the area circled in blue is the Watch panel.

  • Click “Add Expression” to create a new watch expression.

  • Click “Refresh” to update all existing expressions. Values are automatically updated when code executes.

  • Hover over an expression and click “Delete Expression” to remove it.

Formatting Minified Code for Readability

Click Format {} to transform minified code into a human-readable format.

格式化压缩后的文件

Editing a Script

When fixing a bug, you often need to test the effects of your JavaScript code modifications. Instead of editing the JavaScript code in an external editor and then refreshing the current page to see the results, you can directly edit your JavaScript code in DevTools.

To edit a script, you need to:

  1. Open the file you want to edit in the Sources panel (it will appear in the Editor pane).

  2. Make your changes in the Editor pane.

  3. Press Command + S (Mac) or Ctrl + S (Windows, Linux) to save the changes. DevTools will patch the entire JS file into Chrome’s JavaScript engine.

直接编辑js文件

The blue box in the image above highlights the editor toolbar.

Live Editing a Paused Function

Note: This feature is available starting from Chrome 105 and above.

When the code is paused, you can edit the current function and apply the changes in real time, with the following limitations:

  • You can only edit the function at the top of the Call Stack (i.e., the function where the current breakpoint is located).

  • There must be no recursive calls to the same function in the call stack (otherwise, it would effectively modify functions outside the current call stack).

💡 The truth about live editing a paused function…
When you apply a change, the debugger automatically re-executes the function (similar to the “Re-execute function in the stack” feature mentioned earlier). Therefore, the restrictions on re-executing functions are the same as those for live editing a paused function to take effect. You cannot re-execute WebAssembly, async, or generator (iterator) functions from scratch.

To live edit a function, you need to:

  1. Pause execution using a breakpoint.

  2. Edit the currently paused function where the breakpoint is located.

  3. Press Command/Control+S to apply the changes, and the debugger will automatically re-execute the function.

  4. Resume execution.

In this example, the variables addend1 and addend2 initially have an incorrect string type. As a result, the strings are mistakenly concatenated instead of being numerically added. To fix this issue, the parseInt() function was added during real-time editing.

Searching and Replacing Text in Scripts

If you want to search for a piece of text in a script, you need to:

  1. Open the file in the Sources - Editor panel.

  2. Press Command+F (Mac) or Ctrl+F (Windows, Linux) to open the built-in search bar.

  3. In the search bar, enter the string you want to query:

    Additionally, you can:

    • Click Aa to enable case sensitivity for case-sensitive searches.

    • Click .* to use regex matching.

  4. Press Enter to execute the search. Use the up/down arrows to navigate to the next/previous search result.

搜索字符

To replace the search results you’ve found, you need to:

  1. Open the search bar and click the A→B (manually typed for illustration—see the image below) button, which is the replace function, to substitute the text.

  2. Enter the text you want to replace with, then click Replace or Replace all to proceed.

按下替换按钮

Disable JavaScript

For details, refer to: Disable JavaScript With Chrome DevTools (untranslated).

- EOF -
Originally published at: "Translation" Official Guide Series: JavaScript Debugging Guide (Part 1) - Xheldon Blog