本章內容
- 理解瀏覽器報告的錯誤
- 處理錯誤
- 除錯JavaScript程式碼
錯誤處理
錯誤處理在程式設計中的重要性是一定的。任何有影響力的Web應用程式都需要一套完善的錯誤處理機制。
try-catch語句
try {
// 可能會導致錯誤的程式碼
} catch (e) {
// 在錯誤發生時怎麼處理
}
複製程式碼
錯誤型別
執行程式碼期間可能會發生的錯誤有多種型別。每種錯誤都有對應的錯誤型別,而當錯誤發生時,就會丟擲相應型別的錯誤物件。
ECMA-262定義了下列7種錯誤型別:
-
Error
-
EvalError
-
RangeError
-
SyntaxError
-
ReferenceError
-
TypeError
-
URIError
-
RangeError型別的錯誤在數值超出相應範圍時觸發:
try {
let items1 = new Array(-20);
var items2 = new Array(Number.MAX_VALUE);
} catch (e) {
console.dir(e)
}
複製程式碼
- 在找不到物件的情況下,會發生ReferenceError(這種情況下,會直接導致人所共知的“object expected”瀏覽器錯誤)。通常,在訪問不存在的變數時,就會發生這種錯誤:
try {
let obj = x;
} catch (e) {
console.dir(e)
}
複製程式碼
- SyntaxError型別, 當我們把語法錯誤的JavaScript字串傳入eval()函式時,就會導致此類錯誤。
eval('a++ b')
複製程式碼
- TypeError型別在JavaScript中會經常用到,在變數中儲存著意外的型別時,或者在訪問不存在的方法時,都會導致這種錯誤。錯誤的原因雖然多種多樣。**最常發生型別錯誤的情況,就是傳遞給函式的引數事先未經檢查,結果傳入型別與預期型別不相符。
Function.prototype.toString.call('name') // 丟擲typeError
複製程式碼
- 在使用encodeURI()或decodeURI(),而URI格式不正確時,就會導致URIError錯誤。這種錯誤很少見,因為前面說的這兩個函式的容錯性非常高。
利用不同錯誤型別,可以獲悉更多有關異常的資訊,從而有助於對錯誤作出恰當的處理。
try {
// ......
} catch (e) {
if (e instanceof TypeError) {
// 處理型別錯誤
} else if (e instanceof ReferenceError) {
// 處理引用錯誤
} else {
// 處理其他型別的錯誤
}
}
複製程式碼
在跨瀏覽器程式設計中,檢查錯誤型別是確定處理方式的最簡便途徑;包含在message屬性中的錯誤訊息會因瀏覽器而異。
合理使用try-catch
使用try-catch最適合處理那些我們無法控制的錯誤。假設你在使用一個大型JavaScript庫中的函式,該函式可能會有意無意地丟擲一些錯誤。由於我們不能修改這個庫的原始碼,所以大可將對該函式的呼叫放在try-catch語句當中。
丟擲錯誤
與try-catch語句相配的還有一個throw操作符,用於隨時丟擲自定義錯誤。
throw new Error('Something bad happend')
複製程式碼
上面這行程式碼丟擲一個通用錯誤,帶有一條自定義錯誤訊息。也可以像下面使用其他錯誤型別,也可以模擬出類似的瀏覽器錯誤。
throw new SyntaxError('I dont like your syntax');
throw new TypeError('What type of variable do you take me for?');
throw new RangeError('Sorry, you just dont have the range')
throw new EvalError('That doesnt evaluate.')
throw new URIError('Uri, is that you?');
throw new ReferenceError('You didnt cite your references properly');
複製程式碼
錯誤事件
window.onerror = function (message, url, line) {
// 處理錯誤
}
複製程式碼
處理錯誤的策略
略
常見的錯誤型別
錯誤處理的核心,是首先要知道程式碼裡會發生什麼錯誤。由於JavaScript是鬆散型別的,而且也不驗證函式的引數,因此錯誤只會在程式碼執行期間出現。一般需要關注三種錯誤:
- 型別轉換錯誤
- 資料型別錯誤
- 通訊錯誤
型別轉換錯誤
全等操作符知道要比較的是兩種不同的資料型別,因而直接返回false。
資料型別錯誤
Javascript是鬆散型別的,也就是說,在使用變數和函式引數之前,不會對它們進行比較以確保它們的資料型別正確。
通訊錯誤
Javascript與伺服器之間的任何一次通訊,都有可能會產生錯誤。
- 第一種通訊錯誤與格式不正確的URL或傳送的資料有關。最常見的問題是在將資料傳送給伺服器之前,沒有使用encodeURIComponent()對資料進行編碼。
function addQueryString(url, name, value) {
if (url.indexOf('?') === -1) {
url += '?';
} else {
url += '&';
}
url += `&${encodeURIComponent(name)} = ${encodeURIComponent(value)}`;
return url;
}
複製程式碼
使用這個函式而不是手工構建URL,可以確保編碼正確並避免相關錯誤。
區分致命錯誤和非致命錯誤
任何錯誤處理策略中最重要的一部分,就是確定錯誤是否致命。對於非致命錯誤,可以根據下列一或多個條件來確定:
- 不影響使用者的主要任務
- 隻影響頁面的一部分
- 可以恢復
- 重複相同操作可以消除錯誤
把錯誤記錄到伺服器
開發Web應用程式中的一種常見的做法,就是集中儲存錯誤日誌,以便查詢重要錯誤的原因。
要建立這樣一種JavaScript錯誤記錄系統,首先需要在伺服器上建立一個頁面,用於處理錯誤資料。這個頁面的作用無非就是從查詢字元中取得資料,然後再將資料寫入錯誤日誌中。這個頁面可能會使用如下所示的函式:
function logError (lev, msg) {
let img = new Image();
img.src = `log.php?e`
}
複製程式碼
這個logError()函式接收兩個引數:表示嚴重程度的數值或字串及錯誤訊息。其中使用了Image物件來傳送請求,這樣做非常靈活,主要表現如下幾方面:
- 所有瀏覽器都支援Image物件,包括那些不支援XMLHttpRequest物件。
- 可以避免跨域限制。
- 在記錄錯誤的過程中出問題的概率比較低。大多數Ajax通訊都是由JavaScript庫提供的包裝函式來處理的,如果庫程式碼本身有問題,而你還在依賴該庫記錄錯誤,可想而知,錯誤訊息是不可能得到記錄的。
小結
錯誤處理至關重要。不能提前預測到可能發生的錯誤,不能提前採取恢復策略,可能導致較差的使用者體驗。
下面是幾種避免瀏覽器響應JavaScript錯誤的方法。
- 在可能發生錯誤的地方使用try-catch語句,這樣你還有機會以適當的方式對錯誤給出響應。
- 使用window.onerror事件處理程式,這種方式可以接受try-catch不能處理的所有錯誤。
另外,對任何Web應用程式都應該分析可能發生的錯誤。JavaScript中發生錯誤的主要原因如下。
- 型別轉換
- 未充分檢測資料型別
- 傳送給伺服器或從伺服器接收到的資料有錯誤