練習 - 使用 Visual Studio Code 進行偵錯

已完成

是時候開始實踐剛學習到的偵錯工具知識了。 看來我們正好有個完美機會。 我們正在自家的 Tailwind Traders 應用程式中開發新功能,其中可以多種貨幣來顯示產品的價格。 某位同事為此功能撰寫了一些程式碼,卻苦於找不出程式碼哪裡發生問題。 讓我們來協助他。

在 Visual Studio 工作區中建立 JavaScript 檔案

在此練習中,您需要使用 JavaScript 檔案來練習偵錯。 若要使用偵錯工具啟動控制項,JavaScript 檔案必須位於 Visual Studio 工作區中。

應用程式的目標是設定三種貨幣、美元、歐元和日元之間的匯率。 然後,我們要以其他貨幣顯示 10 EUR 的價值,並取得小數點後兩位數的結果。 程式需針對每種新增的貨幣,計算其他所有貨幣的匯率。

  1. 在 Visual Studio Code 中,於 ./nodejs-debug/ 子資料夾內建立名為 mycurrency.js 的檔案。

  2. 將下列程式碼貼入新的檔案編輯器中:

    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');
    
  3. 若要儲存檔案,請按下 Ctrl+S (Windows、Linux) 或 Cmd+S (Mac)。

建立啟動設定

我們將大量使用偵錯工具,因此需要為應用程式建立啟動設定。

  1. 在 Visual Studio Code 的 [ 執行 ] 索引標籤上,選取 [建立 launch.json 檔案 ],然後選取 Node.js 調試程式。

    Visual Studio Code 會在工作區根中建立 .vscode/launch.json 組態檔,並開啟啟動檔案進行編輯。

    Screenshot of generated launch configuration.

    根據預設,系統會建立啟動設定來執行目前已開啟的檔案。 在此範例中,此開啟檔案為 mycurrency.js。 您可以修改啟動組態,自訂程式在您進行偵錯時應該如何啟動。

  2. 在啟動組態中,檢視 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} 表示工作區的根。
  3. 關閉 .vscode/launch.json 檔案。

注意

您可以選取右下角的 [新增組態],為專案建立不同的啟動組態。

分析問題

請確定您的 Visual Studio Code 環境已準備好監視偵錯流程:

  • 偵錯工具面板應該會於左側開啟。 使用左側的 [執行] 索引標籤圖示來切換面板的可見度。
  • 偵錯主控台應該會在底部開啟。 您可以選取 [檢視]>[偵錯主控台],或按下 Ctrl+Shift+Y++ (Windows、Linux) 或 Cmd+Shift+Y++ (Mac) 來開啟主控台。

您現在已可準備開始偵錯。

在偵錯工具啟動控制項中,啟動已啟用偵錯功能的程式 (綠色箭號)。

Screenshot of the Start debugging button in Visual Studio Code.

程式應該很快便會完成。 這是正常的,因為您尚未新增任何中斷點。

您應該會在偵錯主控台中看到這段文字,以及例外狀況。

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", 設定至啟動組態檔,以增加記錄輸出。
  • 設定 [記錄點],而不是中斷點,以避免停止程式執行。 記錄點不會「中斷」到偵錯工具,而是將訊息記錄到主控台。 在偵錯無法暫停或停止的生產伺服器時,記錄點特別適合插入記錄。

修正數字顯示

我們會從第一個錯誤開始修正。 由於此程式碼不是您所撰寫,且程式碼中呼叫了各種函式,因此我們要先使用逐步執行來了解執行流程。

使用中斷點和逐步執行

若要新增斷點,請在 上的第 39printForeignValues(10, 'EUR');左邊界中選取 。

Screenshot of the breakpoint location in the code.

再次開始偵錯,並使用 [逐步執行] 偵錯控制項逐步執行 printForeignValues() 函式:

Screenshot of the Step into button.

檢查變數狀態

現在花點時間檢查 [變數] 面板上各個變數值。

Screenshot of the Variables pane.

  • valuesourceCurrency 變數的值為何?
  • 針對 rates 變數,您是否看到三個預期的機碼,USDEURJPY

使用 [逐程序] 偵錯控制項逐步進行,直到變數 convertedValue 設定完成為止:

Screenshot of the Step over button.

使用 [逐程序] 控制項五次之後,您應該會看到 convertedValue 變數的值被設定為如預期的 11.363636363636365

如果我們再執行一次逐程序,就會看到 displayValue 變數的值。 此值應該是格式化字串,以兩位數 11.36 顯示。

然後,我們可得出程式到目前為止,convertToCurrency()formatValueForDisplay() 函式看似正確,並傳回預期的結果。

更正錯誤

使用 [逐步執行] 控制項一次,來觸及對 console.info() 函式的呼叫。 仔細查看此程式碼。 您有在這裡看到錯誤嗎?

我們需要使用 displayValue 變數來修正此程式錯誤,而非 convertedValue 變數來列印值。

  1. 更新您的 currency.js 檔案,以使用正確的變數名稱。 將對第 32 行 console.info() 函式的呼叫變更為使用 displayValue 變數,而非 convertedValue 變數:

    console.info(`- ${displayValue} ${targetCurrency}`);
    
  2. 將變更儲存至檔案。

  3. 重新啟動程式。

檢查程式是否正確將 USD 值顯示為 11.36。 第一個錯誤:已解決。

找出損毀的原因

現在讓我們來了解程式為何損毀。

  1. 在您的 currency.js 檔案中,移除您在第 39 行上設定的中斷點。

  2. 在 [中斷點] 窗格中勾選 [Uncaught Exceptions] 方塊,以強制程式在引發例外狀況之後暫停。

  3. 再次在偵錯工具中執行程式。

程式應會在例外狀況發生時暫停,並在編輯器視窗中間顯示大型錯誤報告。

Screenshot of the exception message shown in Visual Studio Code.

查看執行停止的那一行,並注意例外狀況訊息 TypeError: Cannot read property 'toFixed' of undefined。 根據該訊息,即可推斷 value 參數函式具有 undefined 值,而非數字。 此錯誤是造成例外狀況的原因。

倒轉呼叫堆疊

您在錯誤訊息下方看到的「堆疊追蹤」,可能有點不易理解。 好消息是,Visual Studio Code 可為您處理函式呼叫堆疊。 根據預設,其只會在 [呼叫堆疊] 窗格中顯示有意義的資訊。 讓我們使用呼叫堆疊資訊來尋找導致此例外狀況的程式碼。

我們知道例外狀況是在呼叫 formatValueForDisplay() 函式時擲回。

  1. 前往偵錯工具面板的 [呼叫堆疊] 窗格。

  2. 若要查看呼叫 formatValueForDisplay() 函式的位置,請按兩下其底下的函式,也就是 printForeignValues 函式。

    Visual Studio Code 前往 currency.js 檔案中的 printForeignValues 函式,其中呼叫了 formatValueForDisplay() 函式:

    const displayValue = formatValueForDisplay(convertedValue);
    

仔細檢查此程式碼。 引發例外狀況的參數是來自 convertedValue 變數。 您必須找出讓此參數值變成 undefined 的時間點。

其中一個選項是在此行新增中斷點,並在每次停在這一行的中斷點時檢查變數。 但是,我們不知道何時可能出現錯誤的值,且在複雜的程式中,這種偵錯方法可能會很麻煩。 讓我們看看替代方法。

新增條件中斷點

在我們的案例中,只有在 convertedValue 變數的值為 undefined 時,才能讓偵錯工具於此中斷點停止。 幸好 Visual Studio Code 可以使用滑鼠右鍵選項來執行此動作。

  1. 在您的 currency.js 檔案中,於第 31 行的左邊界按一下滑鼠右鍵,並選取 [新增條件式中斷點]。

    Screenshot of setting a conditional breakpoint in Visual Studio Code.

  2. 以滑鼠右鍵按一下之後,請輸入下列條件來觸發中斷點,然後按下 [Enter]:

    `convertedValue === undefined`
    
  3. 重新啟動程式。

程式現在應該會在第 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 變數上方以加以查看:

Screenshot of peeking at the rates variable value.

您嘗試取得從 EURJPY 的匯率,但若展開 EUR 值,則只會看到 USD 的轉換率。 JPY 的轉換率遺失了。

修正消失的轉換率

現在您知道有些轉換率遺失了,讓我們來了解原因。 若要移除所有現有中斷點,請選取 [中斷點] 窗格中的 [移除所有中斷點] 圖示。

Screenshot of the button to remove all breakpoints.

監看匯率變數

讓我們設定中斷點來監看 rates 變數。

  1. 在您的 currency.js 檔案中,於 setExchangeRate(0.88, 'USD', 'EUR'); 函式的第 37 行新增中斷點。

  2. 在 [監看式] 窗格中,選取 [加號],然後輸入 rates

    每次變更 rates 變數的值時,更新的值會顯示在 [監看式] 窗格中。

  3. 重新啟動程式。

  4. 當中斷點在第一次呼叫 setExchangeRate() 函式停止時,請使用 [逐程序] 控制項。

  5. 在 [監看式] 窗格中,查看 rates 變數的值。

    此時,如我們所預期,USDEUR 有符合的相對轉換率。

  6. 再次在對 setExchangeRate() 函式的第二次呼叫中執行逐程序。

    您會看到 USDJPY 有符合的相對轉換率,但 EURJPY 之間卻沒有。

是時候查看 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;
}

此函式的關鍵在於最後那兩行。 看來您找到第二個錯誤了! 轉換率只會設定於 sourceCurrencytargetCurrency 變數之間。 此程式也需要計算已新增之其他貨幣的轉換率。

修正程式碼

讓我們針對轉換率問題修正程式碼。

  1. 更新您的 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);
      }
    }
    
  2. 將變更儲存至檔案。

更新的程式碼會為 sourceCurrencytargetCurrency 以外的任何貨幣設定轉換率。 程式會使用 sourceCurrency 的轉換率來推斷其他貨幣與 targetCurrency 之間的轉換率。 程式碼接下來會據此設定其他貨幣的轉換率。

注意

此修正只有在 sourceCurrency 和其他貨幣間的匯率已經存在時才有效,但在這個案例中是可接受的限制。

測試修正

讓我們來測試變更。

  1. 移除所有中斷點和監看變數。

  2. 重新啟動程式。

您現在應該能在主控台中看到預期的結果,且沒有任何損毀。

The value of 10 EUR is:
- 11.36 USD
- 1220.45 JPY

這樣就大功告成了。 您已修正此程式碼。 您現在可以使用 Visual Studio Code 對事前不知道的程式碼進行有效地偵錯了。 做得好!

清除開發容器

完成專案之後,您可能想要清除開發環境,或使其回到一般狀態。

  • 遠端開發 (瀏覽器)
  • 本機開發 (Docker)

刪除 GitHub Codespaces 環境,可確保您可將您為帳戶取得的每個核心免費時數權利數量最大化。

重要

如需 GitHub 帳戶權利的詳細資訊,請參閱 GitHub Codespaces 每月包含的儲存體和核心時數

  1. 登入 GitHub Codespaces 儀表板 (https://github.com/codespaces)。

  2. 找出您目前從 GitHub 存放庫來源的 MicrosoftDocs/node-essentials Codespaces。

    Screenshot of all the running Codespaces including their status and templates.

  3. 開啟 Codespace 的操作功能表,然後選取 [刪除]。

    Screenshot of the context menu for a single codespace with the delete option highlighted.