概覽#
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 用
const、let宣告的變數。 - 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 會看到 hello 與 world:
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 習慣。