JavaScript Object Oriented Programming - Exceptions

JavaScript Object Oriented Programming - Exceptions JavaScript Object Oriented Programming - Exceptions

「例外(Exceptions)」是一個特別且重要的處理錯誤的方法。

Check-first error handling

假設我們要執行func這個method,首先會檢查這個method是否存在。

if (window.func) {
  func();
}

但檢查了是否存在,不代表型別正確,因此我們必須再檢查這個method的型別是否為function。

if (typeof(func) == 'function') {
  func();
}

但意外或錯誤是永遠檢查不完的,例如,這個method的內部可能有些問題。因此,我們可以改用「Try..Catch」來處理。

The try..catch construct

上一個例子是預先把可能發生的問題都檢查一次,例如:存在與否、型別是否正確,如果通過檢查再執行。而「Try..Catch」從另外一個角度來處理 - 先執行,如果有錯再處理。範例如下。

try {
  func();
} 
catch(e) {
  alert(e);
}

這個「e」會告訴我們到底發生什麼問題。

try {
  var a = 5, res = func(a);

  if (res > 0) {
    doA();
  }
  else {
    doB();
  }
} 
catch(e) {
  console.log("name: " + e.name); //name: ReferenceError
  console.log("message: " + e.message); //message: doB is not defined
}

由錯誤訊息可知,doB這個function沒有被定義,因此無法執行而出錯。

The full form of try..catch..finally

try {
   //try statemenets ..
} 
catch(exception) {
   //catch statements ..
} 
finally {
   //finally statements ..
}

首先執行try區塊內的程式碼,如果沒有出錯,則catch區塊會被忽略不執行。如果有錯誤產生,則exception會被設定值,catch區塊會被執行。無論如何,最後,finally區塊都會被執行。

try..catch..finally and return

在try區塊執行return statments,控制權會在執行完finally區塊後才交回給原呼叫的function。所以如下程式碼所示,會先alert 「done」,再alert 「2」。

function inc(a) {
  try {
    return a+1;
  } 
  catch(e) {
    //catch statements...
  } 
  finally {
    alert('done'); //1. alert "done"
  }
}
alert( inc(1) ); //2. alert "2"

The throw statement

基本上,錯誤可以分為兩種:「Programmatic errors」和「Execution flow errors」。

  • Programmatic errors:程式撰寫錯誤,例如:錯字。
  • Execution flow errors:執行時的錯誤,例如:使用者輸入不符合預定格式的資料,程式提示格式有誤並重新輸入。

因此,使用try...catch與throw於Execution flow errors是一個不錯的選擇,範例如下。

try {
  throw 5;
} 
catch(e) {
  alert("Caught: " + e);
}

A validator example

假設我們需要一個年齡的驗證工具,幫助我們檢查使用者輸入的年齡是否合法。如果沒有輸入任何東西,則跳出;如果輸入非文字,則丟出錯誤;如果是數字,則顯示可接受的訊息。

function validatorAge(age){
    if(age === ''){
        return; //no age to valid
    }
    age = +age;

    if(isNaN(age)){
        throw {
            name: 'BadAge',
            message: 'Age out of range'
        }
    }
}

try {
    var age = prompt('Enter your age');
    validatorAge(age);
    alert('The age is accepted');
}
catch(e){
    alert('Error: ' + e.message);
}

Changes in the usage pattern

除了try...catch的用法,當然我們也可以使用error-checking的方法。

function validatorAge(age){
    if(age === ''){
        return; //no age to valid
    }
    age = +age;

    if(isNaN(age)){
        return false;
    }
    else{
        return true;
    }
}

var value = prompt('Enter your age'),
    error = validatorAge(value);

if(!error){
    //process error
    alert('Invalid error!');
}
else{
    //success
    alert('The age is accepted');
}

Comparison

  • try..catch較簡潔,可讀性較高。
  • error-checking無法檢查所有的錯誤,但try..catch確可捕捉所有錯誤,所以try..catch是唯一萬無一失的方法。尤其在檢查瀏覽器相容的錯誤的時候。

Exception analysis and rethrow

有時候,程式碼可能會產生不同種類的錯誤。因此,我們使用「if」來選擇適當的動作。結構類似如下:

try {
  // 1. do smth
} 
catch(e) {
    if (e instanceof ValidationError) {
        // 2.1 process e
    } 
    else if (e instanceof PermissionError) {
        // 2.2 process e
    } 
    else {
        // 3. we don't know how to deal with e
        throw e
    }
}

在try區塊可能丟出了某些錯誤,有些錯誤是我們已知的,例如:ValidationError和PermissionError,然後去處理這些已知的e。但有些我們並不確定,因此直接丟出e。這裡丟出的e,需要由外層的try..catch來處理。

Summary

  • try..catch..finally將多種檢查合併在一個try區塊內執行,並將error-handlin分散在catch區塊來做錯誤的處理。
  • try..catch..finally能捕捉和處理所有的錯誤。丟出的錯誤可使用JavaScript-generated或自行定義的。
  • 錯誤建議繼承基本錯誤物件。因此,可用instanceof來檢查錯誤的類型,如上範例檢查ValidationError和PermissionError。

Reference


由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-JavaScript Object Oriented Programming: Exceptions

留言