1. 程式人生 > >javascript錯誤處理與除錯(轉)

javascript錯誤處理與除錯(轉)

JavaScript 在錯誤處理除錯上一直是它的軟肋,如果指令碼出錯,給出的提示經常也讓人摸不著頭腦。

ECMAScript 第 3 版為了解決這個問題引入了 try...catch 和 throw 語句以及一些錯誤型別,讓開發人員更加適時的處理錯誤。

一.瀏覽器錯誤報告

隨著瀏覽器的不斷升級,JavaScript 程式碼的除錯能力也逐漸變強。IE、Firefox、Safari、Chrome 和 Opera 等瀏覽器,都具備報告 JavaScript錯誤的機制。只不過,瀏覽器一般面向的是普通使用者,預設情況下會隱藏此類資訊。

IE:在預設情況下,左下角會出現錯誤報告,雙擊這個圖示,可以看到錯誤訊息對話方塊。如果開啟禁止指令碼除錯,那麼出錯的時候,會彈出錯誤除錯框。設定方法為:工具->InternetOptions 選項->高階->禁用指令碼除錯,取消勾選即可。

Firefox:在預設情況下,錯誤不會通過瀏覽器給出提示。但在後臺的錯誤控制檯可以檢視。檢視方法為:工具->[Web 開發者]->Web 控制檯|錯誤控制檯。除了瀏覽器自帶的,開發人員為 Firefox 提供了一個強大的外掛:Firebug。它不但可以提示錯誤,還可以除錯 JavaScript和 CSS、DOM、網路連結錯誤等。

Safari:在預設情況下,錯誤不會通過瀏覽器給出提示。所以,我們需要開啟它。檢視方法為:顯示選單欄->編輯->偏好設定->高階->在選單欄中顯示開發->顯示 Web 檢查器|顯示錯誤控制器。

Opera:在預設情況下,錯誤會被隱藏起來。開啟錯誤記錄的方式為:顯示選單欄->檢視->開發者工具->錯誤控制檯。

Chrome:在預設情況下,錯誤會被隱藏起來。開啟錯誤記錄的方法為:工具->JavaScript控制檯。

二.錯誤處理

良好的錯誤處理機制可以及時的提醒使用者,知道發生了什麼事,而不會驚慌失措。為此,作為開發人員,我們必須理解在處理 JavaScript錯誤的時候,都有哪些手段和工具可以利用。

try-catch 語句

ECMA262 第 3 版引入了 try-catch 語句,作為 JavaScript 中處理異常的一種標準方式。

try {//嘗試著執行 try 包含的程式碼
  window.abcdefg();//不存在的方法
} catch (e) {//如果有錯誤,執行 catch,e 是異常物件
  alert('發生錯誤啦,錯誤資訊為:' + e);//直接列印呼叫 toString()方法
}

在 e 物件中,ECMA-262 還規定了兩個屬性:message 和 name,分別打印出資訊和名稱。

alert('錯誤名稱:' + e.name);
alert('錯誤名稱:' + e.message);

PS:Opera9 之前的版本不支援這個屬性。並且 IE 提供了和 message 完全相同的description 屬性、還添加了 number 屬性提示內部錯誤數量。Firefox 提供了 fileName(檔名)、lineNumber(錯誤行號)和 stack(棧跟蹤資訊)。Safari 添加了 line(行號)、sourceId(內部錯誤程式碼)和 sourceURL(內部錯誤 URL)。所以,要跨瀏覽器使用,那麼最好只使用通用的 message。

finally 子句

finally 語句作為 try-catch 的可選語句,不管是否發生異常處理,都會執行。並且不管 try或是 catch 裡包含 return 語句,也不會阻止 finally 執行。

複製程式碼

try {
  window.abcdefg();
} catch (e) {
  alert('發生錯誤啦,錯誤資訊為:' + e.stack);
} finally {//總是會被執行
  alert('我都會執行!');
}

複製程式碼

PS:finally 的作用一般是為了防止出現異常後,無法往下再執行的備用。也就是說,如果有一些清理操作,那麼出現異常後,就執行不到清理操作,那麼可以把這些清理操作放到finally 裡即可。

錯誤型別

執行程式碼時可能會發生的錯誤有很多種。每種錯誤都有對應的錯誤型別,ECMA-262定義了 7 種錯誤型別:

1.Error
2.EvalError
3.RangeError
4.ReferenceError
5.SyntaxError
6.TypeError
7.URIError

其中,Error 是基型別(其他六種型別的父型別),其他型別繼承自它。Error型別很少見,一般由瀏覽器丟擲的。這個基型別主要用於開發人員丟擲自定義錯誤。

PS:丟擲的意思,就是當前錯誤無法處理,丟給另外一個人,比如丟給一個錯誤物件。 

new Array(-5);//丟擲 RangeError(範圍)

錯誤資訊為:RangeError: invalid array length(無效的陣列的長度) 

PS:RangeError 錯誤一般在數值超出相應範圍時觸發 

var box = a;//丟擲 ReferenceError(引用)

錯誤資訊為:ReferenceError: a is not defined(a 是沒有定義的) 

PS:ReferenceError 通常訪問不存在的變數產生這種錯誤 

a $ b;//丟擲 SyntaxError(語法)

錯誤資訊為:SyntaxError: missing ; before statement(失蹤;語句之前) 

PS:SyntaxError 通常是語法錯誤導致的 

new 10;//丟擲 TypeError(型別 )

錯誤資訊為:TypeError: 10 is not a constructor(10 不是一個建構函式) 

PS:TypeError 通常是型別不匹配導致的
PS:EvalError 型別表示全域性函式 eval()的使用方式與定義的不同時丟擲,但實際上並不能產生這個錯誤,所以實際上碰到的可能性不大。
PS:在使用 encodeURI()和 decodeURI()時,如果 URI 格式不正確時,會導致 URIError錯誤。但因為 URI 的相容性非常強,導致這種錯誤幾乎見不到。

alert(encodeURI('李炎恢')); 

利用不同的錯誤型別,可以更加恰當的給出錯誤資訊或處理。

複製程式碼

[task]try {
  new 10;
} catch (e) {
  if (e instanceof TypeError) {//如果是型別錯誤,那就執行這裡
    alert('發生了型別錯誤,錯誤資訊為:' + e.message);
    } else {
  alert('發生了未知錯誤!');
  }
}

複製程式碼

善用 try-catch

在明明知道某個地方會產生錯誤,可以通過修改程式碼來解決的地方,是不適合用try-catch的。或者是那種不同瀏覽器相容性錯誤導致錯誤的也不太適合,因為可以通過判斷瀏覽器或者判斷這款瀏覽器是否存在此屬性和方法來解決。

複製程式碼

try {
  var box = document.getElementbyid('box'); //單詞大小寫錯誤,導致型別錯誤
} catch (e) {//這種情況沒必要 try-catch
  alert(e);
} 

try {
  alert(innerWidth);//W3C 支援,IE 報錯
} catch (e) {
  alert(document.documentElement.clientWidth); //相容 IE
}

複製程式碼

PS:常規錯誤和這種瀏覽器相容錯誤,我們都不建議使用 try-catch。因為常規錯誤可以修改程式碼即可解決,瀏覽器相容錯誤,可以通過普通 if 判斷即可。並且 try-catch 比一般語句消耗資源更多,負擔更大。所以,在萬不得已,無法修改程式碼,不能通過普通判斷的情況下才去使用 try-catch,比如後面的 Ajax 技術。

丟擲錯誤

使用 catch 來處理錯誤資訊,如果處理不了,我們就把它丟擲丟掉。丟擲錯誤,其實就是在瀏覽器顯示一個錯誤資訊,只不過,錯誤資訊可以自定義,更加精確和具體。

複製程式碼

try {
  new 10;
} catch (e) {
  if (e instanceof TypeError) {
    throw new TypeError('例項化的型別導致錯誤!'); //直接中文解釋錯誤資訊
  } else {
    throw new Error('丟擲未知錯誤!');
  }
}

複製程式碼

PS:IE 瀏覽器只支援 Error 丟擲的錯誤,其他錯誤型別不支援。

三.錯誤事件

error 事件是當某個 DOM 物件產生錯誤的時候觸發。

addEvent(window, 'error', function () {
alert('發生錯誤啦!')
});
new 10;//寫在後面 
<img src="123.jpg" onerror="alert('影象載入錯誤!')" />

四.錯誤處理策略

由於 JavaScript 錯誤都可能導致網頁無法使用,所以何時搞清楚及為什麼發生錯誤至關重要。這樣,我們才能對此採取正確的應對方案。
常見的錯誤型別
因為 JavaScript 是鬆散弱型別語言,很多錯誤的產生是在執行期間的。一般來說,需要關注 3 種錯誤:

1.型別轉換錯誤;2.資料型別錯誤;3.通訊錯誤,這三種錯誤一般會在特定的模式下或者沒有對值進行充分檢查的情況下發生。

4.1 型別轉換錯誤

在一些判斷比較的時候,比如陣列比較,有相等和全等兩種:

alert(1 == '1');//true
alert(1 === '1');//false
alert(1 == true);//true
alert(1 === true);//false

PS:由於這個特性,我們建議在這種會型別轉換的判斷,強烈推薦使用全等,以保證判斷的正確性。

var box = 10;//可以試試 0
if (box) {//10 自動轉換為布林值為 true
  alert(box);
}

PS:因為 0 會自動轉換為 false,其實 0 也是數值,也是有值的,不應該認為是 false,所以我們要判斷 box 是不是數值再去列印。

var box = 0;
if (typeof box == 'number') {//判斷 box 是 number 型別即可
  alert(box);
}

PS:typeof box == 'number'這裡也是用的相等,沒有用全等呀?原因是 typeof box本身返回的就是型別的字串,右邊也是字串,那沒必要驗證型別,所以相等就夠了。資料型別錯誤

由於 JavaScript 是弱型別語言,在使用變數和傳遞引數之前,不會對它們進行比較來確保資料型別的正確。所以,這樣開發人員必須需要靠自己去檢測。

function getQueryString(url) {//傳遞了非字串,導致錯誤
  var pos = url.indexOf('?');
  return pos;
}
alert(getQueryString(1));

PS:為了避免這種錯誤的出現,我們應該使用型別比較。

複製程式碼

function getQueryString(url) {
  if (typeof url == 'string') {//判斷了指定型別,就不會出錯了
    var pos = url.indexOf('?');
    return pos;
  }
}
alert(getQueryString(1));

複製程式碼

對於傳遞引數除了限制數字、字串之外,我們對陣列也要進行限制。

複製程式碼

function sortArray(arr) {
  if (arr) {//只判斷布林值遠遠不夠
    alert(arr.sort());
  }
}
var box = [3,5,1];
sortArray(box);

複製程式碼

PS:只用 if (arr)判斷布林值,那麼數值、字串、物件等都會自動轉換為 true,而這些型別呼叫 sort()方法比如會產生錯誤,這裡提一下:空陣列會自動轉換為 true 而非 false。

複製程式碼

function sortArray(arr) {
  if (typeof arr.sort == 'function') {//判斷傳遞過來 arr 是否有 sort 方法
    alert(arr.sort());//就算這個繞過去了
    alert(arr.reverse());//這個就又繞不過去了
  }
}
 
var box = {//建立一個自定義物件,新增 sort 方法
  sort : function () {}
};
sortArray(box);

複製程式碼

PS:這斷程式碼本意是判斷 arr 是否有 sort 方法,因為只有陣列有 sort 方法,從而判斷 arr是陣列。但忘記了,自定義物件添加了 sort 方法就可以繞過這個判斷,且 arr 還不是陣列。

複製程式碼

function sortArray(arr) {
  if (arr instanceof Array) {
    alert(arr.sort());
  }
}
var box = [3,5,1];
sortArray(box);

複製程式碼

通訊錯誤

在使用 url 進行引數傳遞時,經常會傳遞一些中文名的引數或 URL地址,在後臺處理時會發生轉換亂碼或錯誤,因為不同的瀏覽器對傳遞的引數解釋是不同的,所以有必要使用編碼進行統一傳遞。

比如:?user=李炎恢&age=100

var url = '?user=' + encodeURIComponent('李炎恢') + '&age=100'; //編碼

PS:在 AJAX 章節中我們會繼續探討通訊錯誤和編碼問題。

五.除錯技術

在 JavaScript 初期,瀏覽器並沒有針對 JavaScript 提供除錯工具,所以開發人員就想出了一套自己的除錯方法,比如 alert()。這個方法可以列印你懷疑的是否得到相應的值,或者放在程式的某處來看看是否能執行,得知之前的程式碼無誤。

//使用 instanceof 判斷是 Array 最為合適 
var num1 = 1;
var num2 = b;//在這段前後加上 alert('')除錯錯誤
var result = num1 + num2;
alert(result);

PS:使用 alert('')來除錯錯誤比較麻煩,重要裁剪和貼上 alert(''),如果遺忘掉沒有刪掉用於除錯的 alert('')將特別頭疼。所以,我們現在需要更好的除錯方法。

將訊息記錄到控制檯

IE8、Firefox、Opera、Chrome 和 Safari 都有 JavaScript 控制檯,可以用來檢視 JavaScript錯誤。對於 Firefox,需要安裝 Firebug,其他瀏覽器直接使用 console 物件寫入訊息即可。
console 物件的方法

PS:這裡以 Firefox 為標準,其他瀏覽器會稍有差異。

複製程式碼

var num1 = 1;
console.log(typeof num1);//得到 num1 的型別
var num2 = 'b';
console.log(typeof num2);//得到 num2 的型別
var result = num1 + num2;
alert(result);//結果是 1b,匪夷所思

複製程式碼

PS:我們誤把 num2 賦值成字串了,其實應該是數值,導致最後的結果是 1b。那麼傳統除錯就必須使用 alert(typeo num1)來看看是不是數值型別,比較麻煩,因為 alert()會阻斷後面的執行,看過之後還要刪,刪完估計一會兒又忘了,然後又要 alert(typeof num1)來加深印象。如果用了 console.log 的話,所有要除錯的變數一目瞭然,也不需要刪除,放著也沒事。

將錯誤丟擲

之前已經將結果錯誤的丟擲,這裡不在贅述。

if (typeof num2 != 'number') throw new Error('變數必須是數值!');

六.除錯工具
IE8、Firefox、Chrome、Opera、Safari 都自帶了自己的除錯工具,而開發人員只習慣了Firefox 一種,所以很多情況下, Firefox 開發除錯,在然後去其他瀏覽器做相容。其實 Firebug工具提供了一種 Web 版的除錯工具:Firebug lite。

以下是網頁版直接呼叫除錯工具的程式碼:直接複製到瀏覽器網址即可。

javascript:(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new%20Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');

還有一種離線版,把 firebug-lite 下載好,載入工具即可,導致最終工具無法執行,其他瀏覽器執行完好。雖然 Web 版本的 Firebug Lite 可以跨瀏覽器使用 Firebug,但除了 Firefox原生的之外,都不支援斷點、單步除錯、監視、控制檯等功能。好在,其他瀏覽器自己的偵錯程式都有。

<script type="text/javascript" src="https://getfirebug.com/firebug-lite.js"></script>

PS:Chrome 瀏覽器必須在伺服器端方可有效。測試也發現,只能簡單除錯,如果遇到錯誤,系統不能自動丟擲錯誤給 firebug-lite。

1.設定斷點

我們可以選擇 Script(指令碼),點選要設定斷點的 JS指令碼處,即可設定斷點。當我們需要除錯的時候,從斷點初開始模擬執行,發現程式碼執行的流程和變化。

2.單步除錯

設定完斷點後,可以點選單步除錯,一步步看程式碼執行的步驟和流程。上面有五個按鈕:

重新執行:重新單步除錯
斷繼:正常執行程式碼
單步進入:一步一步執行流程
單步跳過:跳到下一個函式塊
單步退出:跳出執行到內部的函式

3.監控

單擊“監控”選項卡上,可以檢視在單步進入是,所有變數值的變化。你也可以新建監
控表示式來重點檢視自己所關心的變數。

4.控制檯

顯示各種資訊。之前已瞭解過。

PS:其他瀏覽器除 IE8 以上均可實現以上的除錯功能,大家可以自己常識下。而我們主要採用 Firebug進行除錯然後相容到其他瀏覽器的做法以提高開發效率。

http://websqq.org/archives/1487