JavaScript Object Oriented Programming: Exceptions
09 Feb 2016「例外(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
由於部落格搬遷至此,因此在這裡放了一份,以便閱讀;部份文章片段也做了些許修改,以期提供更好的內容。