錯誤檢測(1)------try-catch語句 From 《高程3》

0 前言

目前讀到了《高程3》的錯誤檢測部分,如今先挖一個坑,關於錯誤檢測應該寫三篇總結:firebug檢測錯誤和輸出信息;try-catch錯誤捕獲;常見錯誤種類。編程

本篇邏輯思路以下:首先介紹進行錯誤捕獲的try-catch語句;而後介紹常見的錯誤類型,這點會結合firebug來講明;其次介紹try-catch語句和錯誤類型結合使用以捕獲錯誤;可是若是捕獲的錯誤以瀏覽器的語言表示,仍是很難找到錯誤,所以推薦使用throw拋出開發者自定義的錯誤,這樣,錯誤的位置和緣由就會更易發現;最後就錯誤拋出和錯誤捕獲進行了總結討論。數組

1 try-catch簡介

良好的錯誤處理機制可讓用戶和開發者及時獲得提醒,知道到底發生了什麼事,於是不會驚惶失措。ECMA-262 第 3 版引入了 try-catch 語句,做爲 JavaScript 中處理異常的一種標準方式。基本的語法以下所示,瀏覽器

try{
// 可能會致使錯誤的代碼
} catch(error){
// 在錯誤發生時怎麼處理
}

咱們應該把全部可能會拋出錯誤的代碼都放在 try 語句塊中,而把那些用於錯誤處理的代碼放在 catch 塊中。若是 try 塊中的任何代碼發生了錯誤,就會當即退出代碼執行過程,而後接着執行 catch 塊。此時, catch 塊會接收到一個包含錯誤信息的對象。即便你不想使用這個錯誤對象,也要指定一個參數名。這個對象中包含的實際信息會因瀏覽器而異,但共同的是有一個保存着錯誤消息的 message 屬性。這個 message 屬性是惟一一個可以保證全部瀏覽器都支持的屬性,除此以外, IE、 Firefox、 Safari、 Chrome 以及 Opera 都爲錯誤對象添加了其餘相關信息。在跨瀏覽器編程時,最好仍是隻使用 message 屬性。
一個測試例子:
圖片描述架構

只要代碼中包含 finally 子句,則不管 try 或 catch 語句塊中包含什麼代碼——甚至 return 語句,都不會阻止 finally 子句的執行。以下例所示:app

function testFinally() {
  try {
    return 2;
  } catch (error) {
    return 1;
  } finally {
    return 0;
  }
}
function testWithoutFinally() {
  try {
    return 2;
  } catch (error) {
    return 1;
  }
}
console.log(testFinally());//輸出0
console.log(testWithoutFinally());//輸出2

若是提供 finally 子句,則 catch 子句就成了可選的(catch 或 finally 有一個便可)。 IE7 及更早版本中有一個 bug:除非有 catch 子句,不然 finally 中的代碼永遠不會執行。IE8 修復了這個 bug。若是你仍然要考慮 IE 的早期版本,那就只好提供一個 catch 子句,哪怕裏面什麼都不寫。函數

另外一個測試實例:測試

var example = function() {
  try {
    window.someNonexistentFunction();
  } catch (error) {
    console.log(error.name);
    console.log(error.message);
    return 1;
  } finally {
    console.log('everything is over');
  }
};
example();

測試結果:前後執行了try/catch/finally中的語句,問題是爲何會在最後執行一次catch中的return語句?留給本身一個問題吧。
圖片描述this

2 錯誤類型

每種錯誤都有對應的錯誤類型,而當錯誤發生時,就會拋出相應類型的錯誤對象(error object)。ECMA-262 定義了下列 7 種錯誤類型:lua

1) Error 是基類型,其餘錯誤類型都繼承自該類型。所以,全部錯誤類型共享了一組相同的屬性(錯誤對象中的方法全是默認的對象方法)。
2)EvalError 類型的錯誤會在使用 eval()函數而發生異常時被拋出,可是拋出的錯誤不必定是EvalError 類型。若是沒有把 eval()當成函數調用,Firefox 4+和 IE8 對此拋出 TypeError。spa

3)RangeError 類型的錯誤會在數值超出相應範圍時觸發。例如,在定義數組時,若是指定了數組不支持的項數(如-20 或 Number.MAX_VALUE),就會觸發這種錯誤。

4)在找不到對象的狀況下,會發生 ReferenceError(這種狀況下,會直接致使人所共知的"object expected"瀏覽器錯誤)。一般,在訪問不存在的變量時,就會發生這種錯誤。

5) 至於 SyntaxError,當咱們把語法錯誤的 JavaScript 字符串傳入 eval()函數時,就會致使此類錯誤。若是語法錯誤的代碼出如今 eval()函數以外,則不太可能使用 SyntaxError,由於此時的語法錯誤會致使 JavaScript 代碼當即中止執行。

6)TypeError 類型在 JavaScript 中會常常用到,在變量中保存着意外的類型時,或者在訪問不存在的方法時,都會致使這種錯誤。錯誤的緣由雖然多種多樣,但歸根結底仍是因爲在執行特定於類型的操做時,變量的類型並不符合要求所致。最常發生類型錯誤的狀況,就是傳遞給函數的參數事先未經檢查,結果傳入類型與預期類型不相符。

7) 在使用 encodeURI()或 decodeURI(),而 URI 格式不正確時,就會致使 URIError 錯誤。這種錯誤也不多見,由於前面說的這兩個函數的容錯性很是高。

上述測試過程在FF以下:

圖片描述

3 使用try-catch語句捕獲錯誤

要想知道錯誤的類型,能夠像下面這樣在 try-catch 語句的 catch 語句中使用 instanceof 操做符。

try {
  someFunction();
} catch (error){
  if (error instanceof TypeError){
//處理類型錯誤
} else if (error instanceof ReferenceError){
//處理引用錯誤
} else {
//處理其餘類型的錯誤
}
}

在跨瀏覽器編程中,檢查錯誤類型是肯定處理方式的最簡便途徑;而包含在 message 屬性中的錯誤消息會因瀏覽器而異。
使用 try-catch 最適合處理那些咱們沒法控制的錯誤。假設你在使用一個大型 JavaScript 庫中的函數,該函數可能會有意無心地拋出一些錯誤。因爲咱們不能修改這個庫的源代碼,因此大可將對該函數的調用放在 try-catch 語句當中,萬一有什麼錯誤發生,也好恰當地處理它們。

4 throw拋出自定義錯誤

與 try-catch 語句相配的還有一個 throw 操做符,用於隨時拋出自定義錯誤。拋出錯誤時,必需要給 throw 操做符指定一個值,這個值是什麼類型,沒有要求。在遇到 throw 操做符時,代碼會當即中止執行。僅當有 try-catch 語句捕獲到被拋出的值時,代碼纔會繼續執行。

經過使用自定義的內置錯誤類型,能夠更真實地模擬瀏覽器錯誤。每種錯誤類型的構造函數接收一個參數,即實際的錯誤消息。下面是例子。

throw new Error("Something bad happened.");
throw new SyntaxError("I don’t like your syntax.");
throw new TypeError("What type of variable do you take me for?");
throw new RangeError("Sorry, you just don’t have the range.");
throw new EvalError("That doesn’t evaluate.");
throw new URIError("Uri, is that you?");
throw new ReferenceError("You didn’t cite your references properly.");

瀏覽器會像處理本身生成的錯誤同樣,來處理這些代碼拋出的錯誤。換句話說,瀏覽器會以常規方式報告這一錯誤,而且會顯示這裏的自定義錯誤消息。

拋出自定義錯誤的意義:方便快速定位錯誤並糾錯。雖然瀏覽器會本身報錯,可是這些報錯信息在瀏覽器中達不到統一,並且若是出現同種類型錯誤,查找來源是複雜的,尤爲是在數千行代碼中。可是若是你知道可能的代碼錯誤,能夠直接在代碼中添加這些自定義的錯誤,一旦發生這些錯誤,瀏覽器就報出自定義錯誤,關鍵是這個錯誤的位置和類型顯而易見。
實例:一個必須接收數組做爲參數的函數若是接收字符串做爲參數就會報錯。

function process(values){
    values.sort();
    for (var i=0, len=values.length; i < len; i++){
        if (values[i] > 100){
            return values[i];
        }
    }
    return -1;
}
var a = process("string");
 console.log(a);

firebug中結果:
圖片描述
添加自定義錯誤後的代碼:

function process(values){
  if (!(values instanceof Array)){
    throw new Error("process(): Argument must be an array.");
  }
    values.sort();
    for (var i=0, len=values.length; i < len; i++){
        if (values[i] > 100){
            return values[i];
        }
    }
    return -1;
    }

var a = process("string");
 console.log(a);

firebug中結果:
圖片描述

若是 values 參數不是數組,就會拋出一個錯誤。錯誤消息中包含了函數的名稱,以及爲何會發生錯誤的明確描述。若是一個複雜的 Web 應用程序發生了這個錯誤,那麼查找問題的根源也就容易多了。

利用原型鏈還能夠經過繼承 Error 來建立自定義錯誤類型。此時,須要爲新建立的錯誤類型指定 name 和 message 屬性。
一個實例:

//經過繼承Error實現自定義錯誤類型,要定義name和message屬性
   function CustomError(message){       
       this.name = "CustomError";
       this.message = message;
   }
   CustomError.prototype = new Error();

   function process(values){
       //若是出現錯誤,就拋出自定義錯誤類型的對象實例
       if (!(values instanceof Array)){
           throw new CustomError("process(): Argument must be an array.");
       }
       values.sort();
       for (var i=0, len=values.length; i < len; i++){
           if (values[i] > 100){
               return values[i];
           }
       }
       return -1;
   }

   process("string");

firebug顯示自定義類型的錯誤:

圖片描述

5 拋出錯誤與使用 try-catch捕獲錯誤

關於什麼時候該拋出錯誤,而什麼時候該使用 try-catch 來捕獲它們,是一個老生常談的問題。通常來講,應用程序架構的較低層次中常常會拋出錯誤,但這個層次並不會影響當前執行的代碼,於是錯誤一般得不到真正的處理。若是你打算編寫一個要在不少應用程序中使用的 JavaScript 庫,甚至只編寫一個可能會在應用程序內部多個地方使用的輔助函數,我都強烈建議你在拋出錯誤時提供詳盡的信息。而後,便可在應用程序中捕獲並適當地處理這些錯誤。說到拋出錯誤與捕獲錯誤,咱們認爲只應該捕獲那些你確切地知道該如何處理的錯誤。捕獲錯誤的目的在於避免瀏覽器以默認方式處理它們;而拋出錯誤的目的在於提供錯誤發生具體緣由的消息。

相關文章
相關標籤/搜索