概覽#

JavaScript Debugger 的精髓不只在斷點與單步執行(Step Over / Step Into / Step Out),還包含一系列觀察 JavaScript 執行狀態的列表,以及一些影響整個 Debug 流程的設定。

狀態列表#

Watch#

Watch 列表用於持續觀察特定變數值。加入一條 Expression 後,每次暫停或單步執行都會重新計算它,也可以手動點 Refresh watch expressions 圖示強制刷新。

Watch Expression 可能會觸發副作用。當 Expression 是 Function 時較容易察覺,但若副作用藏在物件的 Getter,就很容易被忽略。

Scope#

Scope 列表會顯示目前各個 Scope 中可取得的變數值,DevTools 把 Scope 大致歸類為以下幾種:

  • Local:目前 Function scope 宣告的變數。
  • Block:同一個大括弧區塊內宣告的變數。
  • Closure:目前 Function 可見的外部 Function scope 變數。
  • Script:Global scope 用 constlet 宣告的變數。
  • Global:可以透過 window.x 取得的屬性(var)。

以下程式碼為例,假設停在 console.log,試著想想 a、b、c、d、e 各自會被歸到哪一種 Scope:

var a = "a";
const b = "b";
function outer() {
  const c = "c";
  function inner() {
    const d = "d";
    if (true) {
      const e = "e";
      console.log(a, b, c, d, e); // 斷點
    }
  }
  inner();
}
outer();

變數可能同時屬於多種 Scope,DevTools 會自動歸類其中一種。答案是 Global、Script、Closure、Local、Block。

修改變數#

雙擊變數可以直接修改其值。比較特別的是當 Function 執行到最後一行時,Local scope 會出現 Return value,連回傳值與 Function 本體都可以即時改寫。Scope 列表中的 Function 右鍵還有 Show function definition,可以快速跳到宣告位置。

Call Stack#

每進入一層 Function,調用堆疊(Call Stack)就會疊上一層。以下面為例,在 C 行加斷點再呼叫 outer,Call Stack 會看到 helloworld

function hello() {
  let message = "Hello"; // A
  function world() {
    message = message + " World"; // C 斷點
    console.log(message);
  }
  world(); // B
}

切換堆疊#

Call Stack 內的藍色箭頭代表目前正在觀察的 Function。點擊其他層(例如 hello)就能看到尚未進入 world 之前的狀態。

重新執行(Restart Frame)#

任一層右鍵都有 Restart Frame 選項,可以跳回該 Function 的第一行重新執行。例如在 C 行 Step 一次之後再 Restart Frame,會回到 C 行之前的狀態,便於反覆觀察同一段邏輯。

Console#

暫停期間 Console 仍可隨意執行 JavaScript,存取當下 Scope 的所有變數,例如修改變數值、console.log 輸出物件等等。

Settings#

DevTools 設定中和 Sources 面板有關的選項主要是:

  • Blackboxing:可以把符合特定 Pattern 的檔案列為黑箱(Blackbox),勾選 Blackbox content scripts 則會處理所有 Extension 的 Content scripts。要完全避開 Extension 干擾還是建議用無痕模式。
  • Preferences > Sources:可開關 JavaScript 的 Source Map,預設為開啟。

Blackbox Script#

當一個檔案被列為黑箱(Blackbox)後,逐步執行系列就不會再進入它,但仍可能因為斷點而停在 Blackbox 的程式碼中。

實務上適合 Blackbox 的目標:

  • 寫過完整測試或皆為 Pure function 的內部模組。
  • 第三方套件原始碼。
  • Extension 的 Content scripts。

操作方式:在程式碼右鍵選 Blackbox script,再次點擊則改為 Stop blackboxing

Source Map#

當網頁的程式碼經過壓縮、轉換,幾乎沒辦法直接閱讀,這時 Source Map 會把實際執行的程式碼映射回原始碼。詳細介紹可以回看「Sources - Source Map 與檔案編輯」一章。

Sources 面板程式碼區塊左下角還有一個 Pretty-print({} 圖示),點擊會產生一份 xxx.js:formatted 的格式化版本,遇到過於混亂的程式碼或想閱讀第三方套件時很有幫助。

來自 Pretty-print 或 Source Map 的檔案無法直接編輯生效,要實際改變執行行為,必須修改原始檔案並存檔。

小結#

善用 JavaScript Debugger 的 Watch、Scope、Call Stack、Blackbox、Source Map 等功能,能大幅提升 Debug 的效率。console.log 雖然方便,但下次卡關時試試這些工具,或許會改變你日後的 Debug 習慣。

原文出處#