IE10 Error.stack 讓腳本調試更加方便快捷

在IE10中已經新加入了Error.stack 的支持,可以加快開發人員的腳本調試,并更正錯誤 。尤其是一些難以重現的錯誤 , 如異步操作等 。以下內容來自于微軟IE團隊 , 對于這個特性描述的非常詳細 。
調試應用程序JavaScript 中的結構化錯誤處理依賴于 throw 和 try/catch,開發人員將在其中聲明一個錯誤,并將控制流傳遞至處理錯誤的程序的某一部分 。當某一錯誤被引發時,Chakra,即 Internet Explorer 中的 JavaScript 引擎將捕獲引發該錯誤的調用鏈,這一過程也被稱為調用堆棧 。如果被引發的對象是一個 Error(或者是一個函數,且其原型鏈將導致 Error),那么 Chakra 將創建一個堆棧跟蹤,即可人工讀取的調用堆棧列表 。該列表將被表示為一種屬性 , 即 Error 對象中的 stack 。stack 包含錯誤消息、函數名稱和該函數的源文件位置信息 。這些信息將有助于開發人員了解所調用的函數,甚至查看錯誤的代碼行 , 從而迅速診斷缺陷 。例如,這些信息可能表明傳遞至函數的某一參數為空 , 或為無效類型 。
讓我們一同來查看一個簡單的腳本,并以此展開深入討論 。該腳本試圖計算 (0, 2) 和 (12, 10) 兩點間的距離:

復制代碼代碼如下:
(function () {
’use strict’;
function squareRoot(n) {
if (n0)
throw new Error(’Cannot take square root of negative number.’);
return Math.sqrt(n);
}
function square(n) {
return n * n;
}
function pointDistance(pt1, pt2) {
return squareRoot((pt1.x - pt2.x)(pt1.y - pt2.y));
}
function sample() {
var pt1 = { x: 0, y: 2 };
var pt2 = { x: 12, y: 10 };
console.log(’Distance is: ’pointDistance(pt1, pt2));
}
try {
sample();
}
catch (e) {
console.log(e.stack);
}
})();

該腳本中包含一個缺陷 , 其未調整組件間的差異 。因此,對于某些輸入而言,pointDistance 函數將返回錯誤的結果;而在其他情況中 , 該腳本將導致錯誤發生 。為了理解堆棧跟蹤的含義,讓我們一同來查看 F12 開發人員工具中的錯誤,并查看其腳本選項卡:
【IE10 Error.stack 讓腳本調試更加方便快捷】

IE10 Error.stack 讓腳本調試更加方便快捷

堆棧跟蹤將轉儲至 catch 子句中的控制臺 , 由于其位于堆棧的頂部,因此起源于 squareRoot 函數的錯誤將變得顯而易見 。為了調試這一問題,開發人員無需深入查看堆棧跟蹤;系統已違反 squareRoot 的前置條件,而且只需查看堆棧的上一級,原因將變得十分明了:squareRoot 調用內的子表達式自身應該為 square 的參數 。
調試過程中 , stack 屬性將有助于識別用于設置斷點的代碼 。請記?。耗箍墑褂悶淥椒ɡ床榭吹饔枚顏唬豪? ,如果您將腳本調試程序設置為捕獲異常即中斷的模式,那么您可使用該調試程序來檢查調用堆棧 。對于部署的應用程序,您可考慮在 try/catch 內合并問題代碼 , 以捕獲失敗的調用,并將其記錄于服務器中 。隨后,開發人員可查看調用堆棧 , 以隔離問題區域 。
DOM 異常與 Error.stack此前,我曾注意到被引發的對象必須為 Error 或通過其原型鏈導致 Error 。這是有意而為之;JavaScript 可支持引發任何對象,甚至包括作為異常的基元 。盡管系統可捕獲和檢查所有這些對象 , 但是它們的全部用途并非包含錯誤或診斷信息 。因此,引發過程中僅將更新錯誤的 stack 屬性 。
即便對象為 DOM 異常,它們也不包含可導致 Error 的原型鏈 , 因此它們將不包含 stack 屬性 。在某些應用場景中,您需要執行 DOM 操作,并希望暴露 JavaScript 兼容的錯誤 , 那么您可能希望在 try/catch 數據塊內合并您的 DOM 操作代碼,并在 catch 子句中引發一個新的 Error 對象:

復制代碼代碼如下:
function causesDomError() {
try {
var div = document.createElement(’div’);
div.appendChild(div);
} catch (e) {
throw new Error(e.toString());
}
}

然而,您可能將考慮是否要使用該模式 。這可能是最適用于實用工具庫開發的模式,特別是在您考慮代碼的意圖是否為隱藏 DOM 操作或簡單地實施某一任務的時候 。如果其目的為隱藏 DOM 操作,那么合并操作并引發 Error 可能是我們需要選擇的正確方式 。
性能注意事項堆棧跟蹤的構造始于錯誤對象被引發之時;構造堆棧跟蹤需要查看當前執行堆棧 。為了防止遍歷特大堆棧過程中出現性能問題(甚至可能出現的遞歸堆棧鏈),默認情況下 , IE 僅將收集前十位的堆棧幀 。然而該設置可通過將靜態屬性 Error.stackTraceLimit 設置為另一數值而得以配置 。該設置是全局性的,而且必須在引發錯誤之前 進行變更,否則其將對堆棧跟蹤無效 。
異步異常當某一堆棧是由異步回調(例如 timeout、interval 或 XMLHttpRequest)生成 , 那么異步回調(而非由異步回調創建的代碼)將位于調用堆棧的底部 。這將對跟蹤有問題的代碼產生某些潛在影響:如果您對多個異步回調使用相同的回調函數,那么您將難于通過單獨檢查而確定是哪一回調產生了錯誤 。讓我們對此前的示例稍作修改,我們將避免直接調用 sample(),而是將其放入超時回調:

復制代碼代碼如下:
(function () {
’use strict’;
function squareRoot(n) {
if (n0)
throw new Error(’Cannot take square root of negative number.’);
return Math.sqrt(n);
}
function square(n) {
return n * n;
}
function pointDistance(pt1, pt2) {
return squareRoot((pt1.x - pt2.x)(pt1.y - pt2.y));
}
function sample() {
var pt1 = { x: 0, y: 2 };
var pt2 = { x: 12, y: 10 };
console.log(’Distance is: ’pointDistance(pt1, pt2));
}
setTimeout(function () {
try {
sample();
}
catch (e) {
console.log(e.stack);
}
}, 2500);
})();

一旦執行該代碼段 , 您將發現堆棧跟蹤將出現稍許延遲 。此時 , 您將同時發現堆棧底部并非全局性代碼,而是Anonymous function 。事實上,這并非同一匿名函數,而是傳遞至 setTimeout 的回調函數 。由于您丟失了與掛起回調有關的上下文,因此您可能無法確定調用回調的內容 。如果在某一應用場景中 , 系統注冊了某一回調來處理許多不同按鈕的 click 事件 , 那么您將無法分辨注冊將引用哪一回調 。話雖如此,這一限制作用畢竟有限,因為在大多數情況中,堆棧頂部可能將突出顯示問題區域 。
觀看體驗演示
IE10 Error.stack 讓腳本調試更加方便快捷

了解 Windows 8 Consumer Preview 中 IE10 的使用情況 。您可在 eval 的上下文中執行代碼,如果發生錯誤,您便可檢查出該錯誤 。如果您在 IE10 內運行代碼 , 由于您可將錯誤代碼行懸停于堆棧跟蹤中,因此您也可突出顯示您的代碼行 。您可自行將代碼輸入到代碼區域,或者從列表中的數個示例中進行選擇 。此外,您還可在運行代碼示例時設置 Error.stackTraceLimit 值 。
如欲查看參考材料,請瀏覽有關 Error.stack 和 stackTraceLimit 的 MSDN 文檔 。

相關經驗推薦