练习 - 使用 Visual Studio Code 进行调试
现在是时候实践你新获得的调试知识了。 我们刚好有一个完美的机会。 在我们的 Tailwind Traders 应用中,我们正在开发一项新功能:允许以多种货币显示产品的价格。 一位同事为这一目的编写了一些代码,但却没办法找出是哪里出了问题。 我们来帮帮忙。
在 Visual Studio 工作区中创建 JavaScript 文件
对于本练习,需要一个 JavaScript 文件来练习调试。 若要使用调试器启动控件,JavaScript 文件必须位于 Visual Studio 工作区中。
此应用程序的目标是设置三种货币(USD、EUR 和 JPY)之间的汇率。 然后,我们要显示 10 EUR
对应于其他所有货币的价值,精确到小数点后两位。 对于添加的每种货币,应计算与所有其他货币的汇率。
在 Visual Studio Code 中,在
./nodejs-debug/
子文件夹创建名为mycurrency.js
的文件。将以下代码粘贴到新的文件编辑器中:
const rates = {}; function setExchangeRate(rate, sourceCurrency, targetCurrency) { if (rates[sourceCurrency] === undefined) { rates[sourceCurrency] = {}; } if (rates[targetCurrency] === undefined) { rates[targetCurrency] = {}; } rates[sourceCurrency][targetCurrency] = rate; rates[targetCurrency][sourceCurrency] = 1 / rate; } function convertToCurrency(value, sourceCurrency, targetCurrency) { const exchangeRate = rates[sourceCurrency][targetCurrency]; return exchangeRate && value * exchangeRate; } function formatValueForDisplay(value) { return value.toFixed(2); } function printForeignValues(value, sourceCurrency) { console.info(`The value of ${value} ${sourceCurrency} is:`); for (const targetCurrency in rates) { if (targetCurrency !== sourceCurrency) { const convertedValue = convertToCurrency(value, sourceCurrency, targetCurrency); const displayValue = formatValueForDisplay(convertedValue); console.info(`- ${convertedValue} ${targetCurrency}`); } } } setExchangeRate(0.88, 'USD', 'EUR'); setExchangeRate(107.4, 'USD', 'JPY'); printForeignValues(10, 'EUR');
要保存该文件,请按“Ctrl+S”(Windows、Linux)或“Cmd+S”(Mac)。
创建启动配置
我们会经常用到调试器,因此让我们为应用创建一个启动配置。
在 Visual Studio Code 中的“运行”选项卡上,选择“创建 launch.json 文件”,然后选择 Node.js 调试程序。
Visual Studio Code 会在工作区的根目录中创建
.vscode/launch.json
配置文件,并打开启动文件进行编辑。默认情况下,将创建启动配置以执行当前打开的文件。 在本示例中,打开的文件为
mycurrency.js
。 可以修改启动配置,以自定义程序在调试时的启动方式。在启动配置中,查看
program
属性的值。{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "skipFiles": [ "<node_internals>/**" ], "program": "${workspaceFolder}/nodejs-debug/mycurrency.js" } ] }
${workspaceFolder}
指示工作区的根。
关闭
.vscode/launch.json
文件。
注意
可以通过选择右下角的“添加配置”为项目创建不同的启动配置。
分析问题
确保 Visual Studio Code 环境已准备就绪,可监视调试过程:
- 调试器面板应在左侧打开。 使用左侧的“运行”选项卡图标切换面板的可见性。
- 调试控制台应在底部打开。 可通过选择“查看”>“调试控制台”或按 Ctrl+Shift+Y (Windows、Linux) 或 Cmd+Shift+Y (Mac) 来打开控制台。
现已准备就绪,接下来即可开始调试。
在调试器启动控件中,启动启用了调试的程序(绿色箭头)。
应看到程序快速完成。 这是正常的,因为尚未添加任何断点。
应在调试控制台中看到此文本,后跟一个异常。
The value of 10 EUR is:
11.363636363636365
- 11.363636363636365 USD
/app/node-101/currency.js:23
return value.toFixed(2);
^
TypeError: Cannot read property 'toFixed' of undefined
at formatValueForDisplay (/app/node-101/currency.js:23:16)
at printForeignValues (/app/node-101/currency.js:32:28)
at Object.<anonymous> (/app/node-101/currency.js:40:1)
at Module._compile (internal/modules/cjs/loader.js:959:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
at Module.load (internal/modules/cjs/loader.js:815:32)
at Function.Module._load (internal/modules/cjs/loader.js:727:14)
at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)
at internal/main/run_main_module.js:17:11
此程序所要做的是设置三种货币(USD、EUR、JPY)之间的汇率,并显示 10 EUR
对应于其他所有货币的值,精确到小数点后两位。
在这里可以看到两个 bug:
- 小数点后不止两位。
- 程序崩溃并出现异常,无法显示
JPY
值。
提示
- 将
"outputCapture": "std",
设到你的启动配置文件以增加日志记录输出。 - 设置一个记录点而不是断点,以避免停止程序执行。 日志点不会“闯入”调试器,而是会将消息记录到控制台。 日志点对于在调试无法暂停或停止的生产服务器时的注入日志记录操作特别有用。
修复位数显示
首先,我们将解决第一个 bug。 由于你没有编写此代码,并且调用了许多不同的函数,因此让我们首先使用分布执行来了解执行流。
使用断点并逐步执行
要添加断点,请选择 printForeignValues(10, 'EUR');
上第 39 行的左边距。
再次开始调试,并使用“单步执行”调试控件单步执行 printForeignValues()
函数:
检查变量状态
接下来,花一些时间检查“变量”窗格中的其他变量值。
value
和sourceCurrency
变量的值是什么?- 对于
rates
变量,你是否看到了三个预期的项:USD
、EUR
和JPY
?
要继续单步执行直至设置变量 convertedValue
,请使用“单步跳过”调试控件。
使用“单步跳过”控件五次之后,应会看到 convertedValue
变量的值设置为 11.363636363636365
。
如果再一次单步跳过,我们将看到 displayValue
变量的值。 该值应为显示的带有两位数 11.36
的格式字符串。
然后我们可以得出结论,到目前为止,函数 convertToCurrency()
和 formatValueForDisplay()
似乎是正确的,并返回了预期的结果。
更正错误
使用“单步执行”一次可访问对 console.info()
函数的调用。 仔细检查此代码行。 看到这里的错误了吗?
我们需要通过使用 displayValue
变量而不是 convertedValue
变量以输出值来修复此程序 bug。
更新
currency.js
文件以使用正确的变量名称。 更改对第 32 行上的console.info()
函数的调用以使用displayValue
变量而不是convertedValue
变量:console.info(`- ${displayValue} ${targetCurrency}`);
保存对文件所做的更改。
重启程序。
检查程序是否正确地将值 USD
显示为 11.36
。 第一个 Bug - 已解决。
找出崩溃的原因
现在让我们弄清楚程序崩溃的原因。
在
currency.js
文件中,删除在第 39 行设置的断点。要强制程序在引发异常后暂停,请选中“断点”窗格中的“未捕获的异常”框。
在调试器中再次运行该程序。
此程序应在出现异常时暂停,并在编辑器窗口的中间显示一个大型错误报告。
查看执行停止的行,并注意异常消息 TypeError: Cannot read property 'toFixed' of undefined
。 通过该消息可以推断,value
参数函数的值是 undefined
而不是数字。 此错误是导致异常的原因。
倒回调用堆栈
你在错误消息下面看到的堆栈跟踪可能有点难以理解。 好消息是,Visual Studio Code 会为你处理函数调用堆栈。 默认情况下,“调用堆栈”窗格中只会显示有意义的信息。 让我们使用调用堆栈信息来查找导致此异常的代码。
我们知道,此异常是在调用 formatValueForDisplay()
函数时引发的。
在调试器面板中,转到“调用堆栈”窗格。
若要查看
formatValueForDisplay()
函数的调用位置,请双击其下方的函数,即printForeignValues
函数。Visual Studio Code 转到
currency.js
文件中printForeignValues
函数中调用formatValueForDisplay()
函数的行:const displayValue = formatValueForDisplay(convertedValue);
仔细观察此代码行。 导致异常的参数来自于 convertedValue
变量。 需要找出该参数值在何时变为 undefined
。
一种选择是在此行添加一个断点,并在断点每次在该行停止时检查该变量。 但是,我们不知道何时会出现错误的值,而且在复杂的程序中,这种调试方法可能很麻烦。 让我们看看一个替代方法。
添加条件断点
在本例中,只有当 convertedValue
变量的值为 undefined
时,才能使调试器在此断点处停止。 幸运的是,Visual Studio Code 可以使用鼠标右键选项执行此操作。
在
currency.js
文件的第 31 行的左边距中,右键单击并选择“添加条件断点”。右键单击后,输入以下条件以触发断点,然后按 Enter:
`convertedValue === undefined`
重启程序。
程序现在应在第 31 行停止,我们可以检查调用堆栈值。
观察当前状态
现在,我们花一些时间来分析当前程序的状态。
convertedValue
变量的值来自对convertToCurrency(value, sourceCurrency, targetCurrency)
函数的调用。 我们需要检查此函数调用中的参数值,并确认它们是否正确。具体而言,我们需要检查
value
变量并确认它具有预期值10
。
查看一下 convertToCurrency()
函数的代码。
function convertToCurrency(value, sourceCurrency, targetCurrency) {
const exchangeRate = rates[sourceCurrency][targetCurrency];
return exchangeRate && value * exchangeRate;
}
你知道此代码的结果为 undefined
。 你还知道 value
变量已设置为 10
。 此信息有助于我们了解问题一定出在 exchangeRate
变量的值上。
在 currency.js
文件中,将鼠标悬停在 rates
变量之上进行查看:
你尝试获取 EUR
到 JPY
的汇率,但如果展开 EUR
值,你会发现只有 USD
的转换率。 缺少 JPY
的转换率。
修复缺失的转换率
现在已知缺失某些转换率,让我们来了解原因。 要删除所有现有断点,请在“断点”窗格中选择“删除所有断点”图标。
观察汇率变量
让我们设置一个断点来监视 rates
变量。
在
currency.js
文件中,在setExchangeRate(0.88, 'USD', 'EUR');
函数的第37
行添加一个断点。在“监视”窗格中,选择加号,然后输入
rates
。每次更改
rates
变量的值时,更新后的值都会显示在“监视”窗格中。重启程序。
当断点在第一次调用
setExchangeRate()
函数时停止时,使用“单步跳过”控件。在“监视”窗格中,查看
rates
变量的值。此时可以看到
USD
和EUR
的互汇率相同,符合我们的预期。在第二次调用
setExchangeRate()
函数时再次单步跳过。你会看到
USD
和JPY
的互汇率相等,但并未显示EUR
和JPY
之间的汇率。
现在来看看 setExchangeRate()
函数的代码。
function setExchangeRate(rate, sourceCurrency, targetCurrency) {
if (rates[sourceCurrency] === undefined) {
rates[sourceCurrency] = {};
}
if (rates[targetCurrency] === undefined) {
rates[targetCurrency] = {};
}
rates[sourceCurrency][targetCurrency] = rate;
rates[targetCurrency][sourceCurrency] = 1 / rate;
}
此函数中最重要的行是最后两行。 看来你找到第二个 bug 了! 兑换率仅可在 sourceCurrency
和 targetCurrency
变量之间进行设置。 该计划还需要计算添加的其他货币的兑换率。
修复代码
我们来修复兑换率问题的代码。
更新
currency.js
文件以计算其他货币的兑换率。替换第 12 行和第 13 行的代码:
rates[sourceCurrency][targetCurrency] = rate; rates[targetCurrency][sourceCurrency] = 1 / rate;
使用此更新后的代码:
for (const currency in rates) { if (currency !== targetCurrency) { // Use a pivot rate for currencies that don't have the direct conversion rate const pivotRate = currency === sourceCurrency ? 1 : rates[currency][sourceCurrency]; rates[currency][targetCurrency] = rate * pivotRate; rates[targetCurrency][currency] = 1 / (rate * pivotRate); } }
保存对文件所做的更改。
更新后的代码可设置 sourceCurrency
和 targetCurrency
以外的任何货币的兑换率。 程序使用 sourceCurrency
的兑换率来推断另一种货币与 targetCurrency
之间的汇率。 然后,代码会相应地设置其他货币的兑换率。
注意
只有当 sourceCurrency
和其他货币之间的汇率已经存在时,此修复才有效,在本例中,这是一个可以接受的限制。
测试更正
我们来测试一下更改。
删除所有断点并监视变量。
重启程序。
现在应在控制台中看到预期的结果,而不会出现任何故障。
The value of 10 EUR is:
- 11.36 USD
- 1220.45 JPY
就是这样。 代码修复了。 现在,你可以使用 Visual Studio Code 高效调试事先不了解的代码。 干得不错!
清理开发容器
完成项目后,你可能希望清理开发环境或将其返回到其典型状态。
删除 GitHub Codespaces 环境可确保可以最大程度地提高帐户获得的每核心免费小时数权利。
重要
有关 GitHub 帐户权利的详细信息,请参阅 GitHub Codespaces 每月包含的存储和核心小时数。
登录到 GitHub Codespaces 仪表板 (https://github.com/codespaces)。
找到当前正在运行的、源自
MicrosoftDocs/node-essentials
GitHub 存储库的 Codespaces。打开 codespace 的上下文菜单,然后选择“删除”。