在 Chrome 2.0 中為 JavaScript 偵錯

0 comments
和大多數人一樣,瀏覽器本身對我來說並不重要,它只是瀏覽網頁、使用網路應用的工具而已。相較於瀏覽器的功能性,快速、穩定的基本要素更能貼近我的需求。因此,強調簡潔、快速的 Google Chrome 瀏覽器,很快就在我心中佔據了一個無可取代的地位。

Chrome 不僅滿足使用者對瀏覽器的根本訴求,也從開發人員需求實務上的觀點設計許多的功能,希望能藉以協助開發人員提供更好的使用者經驗。在開發人員的工具中,最令我感興趣的,卻也是對我極度不友善而讓我望之卻步的,莫過於基於命令提示介面的 JavaScript 偵錯工具(JavaScript Debugger)了。當然,這對於既自詡為 Chrome 愛用者,又身兼網頁開發人員的我來說,似乎令人難以悅服。於是,最近我又再度興起要深入研究這個好用的小工具的念頭,如今在此分享自己的學習心得及蒐集的相關資訊,希望能對有興趣的朋友有所幫助。

命令
在 JavaScript 偵錯工具中,可使用的命令會依目前是否處於執行中或是中斷狀態而有所不同。在執行狀態中,也就是網頁尚未執行暫止於中斷點時的狀態下,可以使用的命令如下:
命令說明
b[reak] <function | script:function | script:line | script:line:pos> [condition]在來源中的位置或函式設定中斷點。
break_info [breakpoint #]

bi [breakpoint #]
顯示目前所有中斷點資訊,或是已指定中斷點的資訊。
clear <breakpoint #>移除已指定的中斷點。
h[elp] [command]顯示所有命令的描述,或是已指定命令的詳細描述。
p[rint] <expression>評估運算式並輸出執行結果。
scripts顯示所有可偵錯的指令碼資訊。
註:[] 表示可省略或選擇性項目; <> 表示必須提供的項目。

當網頁暫止於中斷點的狀態時,可以使用的命令如下:
命令說明
args顯示目前函數的參數。
b[reak] [<function | script:function | script:line | script:line:pos> [condition]]在來源中的位置或函式設定中斷點,或是不使用任何參數停止偵錯並終止程式執行。
break_info [breakpoint #]

bi [breakpoint #]
顯示目前所有中斷點資訊,或是已指定中斷點的資訊。
backtrace [from frame #] [to frame #]

bt [from frame #] [to frame #]
顯示目前函式的呼叫堆疊(Call Stack)。
clear <breakpoint #>移除已指定的中斷點。
c[ontinue]繼續執行到下一個中斷點。
dir <expression>顯示物件的詳細資訊。
f[rame] <frame #>顯示目前堆疊框架(Stack Frame)的偵錯資訊,或是已指定堆疊框架的偵錯資訊。
h[elp] [command]

? [command]
顯示所有命令的描述,或是已指定命令的詳細描述。
locals顯示目前堆疊框架中所有變數的值。
n[ext]逐步執行每行指令碼,如果是執行函式呼叫,則會進入函式內的第一行指令碼。
p[rint] <expression>評估運算式並輸出執行結果。
scripts顯示所有可偵錯的指令碼資訊。
source [from line] | [<from line> <num lines>]

ls [from line] | [<from line> <num lines>]
顯示目前堆疊框架的原始碼,或從指定的行號開始顯示。
s[tep]逐步執行每行指令碼,如果是執行函式呼叫,則不會進入函式直接執行。
stepout

so
在目前的函式中,繼續執行指令碼直到函式返回,然後在呼叫函式的返回點中斷。

範例網頁
為了幫助你更快熟悉偵錯命令的應用,將會利用以下範例網頁,用逐步解說的的方式進行偵錯示範。
<html>
<head>
<title>Sample Page</title>
<script type="text/javascript" src="shape.js"></script>
<script type="text/javascript">
function Rectangle(x, y, w, h, cnv) {
Shape.call(this, x, y);
this.width = w;
this.height = h;
this.convas = cnv;
return true;
}

Rectangle.prototype = new Shape();
Rectangle.prototype.draw = function() {
if(this.convas) {
this.convas.innerHTML += "Drawing a Rectangle at:" + this.getCoordinates() +
", width " + this.width + ", height " + this.height + "<br/>";
}
};

function drawRectangle() {
var x = parseInt(document.getElementById("x").value);
var y = parseInt(document.getElementById("y").value);
var w = parseInt(document.getElementById("w").value);
var h = parseInt(document.getElementById("h").value);
var cnv = document.getElementById("cnv");
var rect = new Rectangle(x, y, w, h, cnv);
rect.draw();
}
</script>
</head>
<body>
x:<input id="x" type="text" size="3" /> y:<input id="y" type="text" size="3" />
width:<input id="w" type="text" size="3" /> height:<input id="h" type="text" size="3" />
<input type="button" value="Draw Rectangle" onclick="javascript:drawRectangle();" />
<div id="cnv"></div>
</body>
</html>

在範例網頁中引入一個外部的 js 檔案,其內容如下:
function Shape(x, y) {
this.x = x;
this.y = y;
return true;
}

Shape.prototype = {
getCoordinates : function() {
return "(" + this.x + " ," + this.y + ")";
}
};

當你在 Chrome 中開啟範例網頁後,請按一下 [網頁功能] 功能表,然後在 [開發人員選項] 點選 [為 JavaScript 偵錯] 或是使用鍵盤捷徑 [Ctrl+Shift+L],這時 JavaScript 偵錯工具便會開啟並附加在作用中的分頁。另外,你還需要 [檢視網頁原始碼],除了便於在偵錯期間檢視程式碼外,也可以利用原始碼的行號來設定中斷點。


偵錯逐步解說
在進行偵錯前,我們可以在命令列輸入 "scripts" 來檢視附加在範例網頁的指令碼資訊,如以下輸出結果:
$ scripts
file:///C:/shape.js (lines 11)
file:///C:/test.htm (lines 4-30)
[unnamed] (source:"javascript:void(0)")

如果你要將中斷點設在外部檔案中的 Shape 建構函式,你可以執行如下的命令:
$ break Shape
set breakpoint #1

除了指定函式中斷點外,你也可以指定原始碼的位置來設定中斷點。例如,若要將中斷點設在如下的指令碼行:
var rect = new Rectangle(x, y, w, h, cnv);

那麼,你可以先從 [檢視網頁原始碼] 中得知指令碼行所在行號,然後執行如下的命令:
$ break file:///C:/test.htm:28
set breakpoint #2

然而,上述設定中斷點的方式都屬於無條件中斷,事實上,你還可以選擇性地設定需同時符合特定條件的中斷點:
$ break file:///C:/test.htm:24 x<0
set breakpoint #3

這個命令將會在以下的原始碼位置設定中斷點,且變數 x 的值必須滿足特定條件才會暫止程式執行。
var y = parseInt(document.getElementById("y").value);

當你完成如上的中斷點設定後,便可以使用 "break_info" 檢視所有的中斷點資訊:
$ break_info
Num breakpoints: 3
id=1, hit_count=0, type=function, target=Shape
id=2, hit_count=0, type=script, target=file:///C:/test.htm, line=27
id=3, hit_count=0, type=script, target=file:///C:/test.htm, line=23, condition=x<0

現在,你可以回到範例網頁的頁籤,然後填入必要欄位並按下按紐執行指令碼。如果你在 x 欄位輸入的數值小於零的話,那麼程式就會暫止在第三個中斷點:
paused at breakpoint 3: drawRectangle(), file:///C:/test.htm
24: var y = parseInt(document.getElementById("y").value);

若要繼續執行指令碼,可以視情況選擇使用 "step" 或 "next" 命令來逐行執行指令碼:
$ next
25: var w = parseInt(document.getElementById("w").value);

或是,使用 "continue" 命令繼續執行到下一個中斷點,也就是之前所設定的第二個中斷點:
$ continue
paused at breakpoint 2: drawRectangle(), file:///C:/test.htm
28: var rect = new Rectangle(x, y, w, h, cnv);

當程式中斷執行時,可以輸入 "locals" 命令來檢視目前執行函數中的所有變數值:
$ locals
w = 400
cnv = #<an HTMLDivElement>
x = -10
y = 0
h = 600
rect = undefined

若程式繼續執行,將會暫止在第一個中斷點:
$ continue
paused at breakpoint 1: #.Shape(x=-10, y=0), file:///C:/shape.js
2: this.x = x;

在偵錯過程中,你可以選擇性地切換堆疊框架來進行偵錯。首先,你可以透過 "backtrace" 命令來檢視所有堆疊框架的資訊:
$ backtrace
Frames #0 to #4 of 5:
#00 #<an Object>.Shape(x=-10, y=0) file:///C:/shape.js line 2 column 12 (position 36)
#01 new Rectangle(x=-10, y=0, w=400, h=300, cnv=#<an HTMLDivElement>) file:///C:/test.htm line 7 column 15 (position 58)
#02 drawRectangle() file:///C:/test.htm line 28 column 20 (position 839)
#03 #<an HTMLInputElement>.[anonymous](evt=#<a MouseEvent>) file:///C:/test.htm line 37 column 12 (position 177)
#04 #<an HTMLInputElement>.onclick(evt=#<a MouseEvent>) file:///C:/test.htm line 38 column 4 (position 197)

然後,再使用 "frame" 的命令來切換到指定的堆疊框架,如以下所示:
$ frame 1
#01 Rectangle, undefined
7: Shape.call(this, x, y);

你可以視需要使用 "source" 命令來檢視目前堆疊框架的原始碼:
$ source
5:
6: function Rectangle(x, y, w, h, cnv) {
>>>> Shape.call(this, x, y);
8: this.width = w;
9: this.height = h;
10: this.convas = cnv;
11: return true;
12: }

如果不打算繼續執行指令碼,請使用 "break" 命令停止偵錯並終止程式執行:
$ break
JavaScript execution already stopped.


參考資料:
Sample debug session with Google Chrome JavaScript debuger
Basic information on Chrome's Debugger
Google Chrome JavaScriptデバッガ完全マニュアル

繼續閱讀...