前端的水平線,錯誤處理和除錯

ZhangCheng發表於2018-06-06

本章內容

  • 理解瀏覽器報告的錯誤
  • 處理錯誤
  • 除錯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中發生錯誤的主要原因如下。

  • 型別轉換
  • 未充分檢測資料型別
  • 傳送給伺服器或從伺服器接收到的資料有錯誤

相關文章