
編程語言都會需要完善的錯誤處理策略使得應用程序更為合理的操作錯誤。錯誤處理在服務端的處理較為完善,但是瀏覽器端進展較為緩慢,不同瀏覽器的錯誤處理方式也不同,且默認的錯誤處理方式對用戶也不友好。因此,必須理解各種捕獲和處理錯誤的方式。而在 ECMA-262 的第3版中增加了 try-catch 語句塊和 throw 語句來處理錯誤,以及一些錯誤類型來描述錯誤。
try-catch
ECMA-262 新增的錯誤處理方式與 Java 相似,將可能出錯的代碼放在 try 子句中,而處理錯誤的代碼放在 catch 子句中:
try {
// Possible error code
} catch (e) {
// Error Handler
}
catch 子句中捕獲到的錯誤對象包含發(fā)生錯誤的相關信息,通過該錯誤對象可以獲取錯誤類型的 name 屬性和保存錯誤消息的 message 屬性,e.message 即可調(diào)用。
在使用 try-catch 語句塊時,要知道什么時候使用最好。如果發(fā)生無法控制的錯誤上,就需要使用 try-catch 語句塊來處理錯誤;如果明確知道代碼會發(fā)生某種錯誤,就要采用相應的操作來防止錯誤發(fā)生,而不是使用 try-catch 語句塊來處理錯誤。
finally
在 try-catch 語句塊中,無論是 try 子句執(zhí)行完,還是 catch 子句執(zhí)行完,都可以接著執(zhí)行 finally 子句中的代碼,try 與 catch 都無法阻止,包括 return 語句。
function test(){
try {
return 1;
} catch (e){
throw e;
} finally {
return false;
}
}
上面的方法,最后會返回 false。
<small>注意:主要代碼中包含了 finally 子句,try 塊或 catch 塊中的 return 語句就會被忽略,理解這一點很重要。在使用 finally 時一定要仔細確認代碼的行為。</small>
拋出錯誤
使用 throw 拋出錯誤是另一種錯誤處理方式。throw 拋出任意表達式。如下所示:
throw "Not Value Error"; // String type error
throw 31; // Number type error
throw false; // Boolean type error
throw { name: "JavaScript" }; // Object type error
也可以調(diào)用 Error 構造函數(shù)來生成一個錯誤對象拋出,接收的值為錯誤消息。如下所示:
throw new Error("Null value Error.");
也可以調(diào)用特定的錯誤類型生成一個錯誤對象并拋出。如 SyntaxError、TypeError 等。使用 ES6 中的繼承語法創(chuàng)建錯誤類型也可以,這樣需要提供 name 屬性和 message 屬性。如下所示:
class CustomError extends Error {
constructor(message) {
super(message);
this.name = "CustomError";
this.message = message;
}
}
throw new CustomError("Null Value Error.");
這種方式有助于在捕獲錯誤時更精準地區(qū)分錯誤。
Error
Error 錯誤類型是基本類型,其它錯誤類型基本都是繼承該類型。Error 提供了一個構造方法用來創(chuàng)建實例對象,當發(fā)生錯誤時,Error 通過構造方法創(chuàng)建實例對象并被拋出。
throw new Error("Null Value Error.");
Error 錯誤類型提供了三個屬性:
-
name:字符串類型。表示錯誤名稱。 -
message:字符串類型。表示錯誤信息。 -
stack:字符串類型。表示錯誤的堆棧。
ECMA-262 總共定義了 8 種錯誤類型,除了 Error 通用的錯誤類型外,還有如下幾種類型:
-
EvalError:該錯誤類型會在使用eval()函數(shù)發(fā)生錯誤時拋出。 -
InternalError:該錯誤類型主要會在底層 JavaScript 引擎拋出異常時由瀏覽器拋出。如遞歸過多導致棧溢出。通常并不是需要處理的錯誤。 -
RangeError:該錯誤類型會在數(shù)值變量或參數(shù)越界時拋出。 -
ReferenceError:該錯誤類型會在引用無效時拋出。 -
SyntaxError:該錯誤類型會在eval()在解析代碼的過程中發(fā)生。 -
TypeError:該錯誤類型會在變量或參數(shù)不是預期類型,或者訪問不存在的方法時拋出。 -
URIError:該錯誤類型只會在使用encodeURI()或decodeURI(),并且傳入的URI格式錯誤時發(fā)生。
瀏覽器很少會拋出 Error 類型的錯誤,主要是開發(fā)者拋出的自定義錯誤。而其它錯誤類型可以使用 instanceof 進行判斷。如下所示:
try {
// Possible error code
} catch (e) {
if (e instanceof TypeError) {
// Type Error Handler
} else if (e instanceof ReferenceError) {
// Reference Error Handler
} else {
// Other Error Handler
}
}
error 事件
沒有被 try-catch 捕獲的錯誤都會在 window 對象上觸發(fā) error 事件。該事件會傳入幾個參數(shù):
-
message:錯誤消息。 -
url:發(fā)生錯誤的URL。 -
line:錯誤的行號。 -
colno:錯誤的列號。 -
error:Error對象。
window.onerror = (message, url, line, colno, error) => {
// 可以對錯誤的消息進行處理,這里打印在控制臺上
console.log(message);
return false; // 這里返回 false 來阻止瀏覽器默認報告錯誤的行為
};
通過返回 false,函數(shù)實際上變成整個文檔的 try/catch 語句,可以捕獲所有未處理的運行時錯誤。適當?shù)?try-catch 語句塊意味著不會有錯誤到達瀏覽器這個 層次,因此就不會觸發(fā) error 事件。當返回 true,會阻止執(zhí)行默認事件處理函數(shù)。
而有一些資源加載失敗,會觸發(fā)一個 error 事件,這個事件遵循 DOM 格式。因此可以使用 addEventListener 捕獲。
const image = new Image();
image.addEventListener("error", (event) => {
console.log("Image not loaded!");
});
image.src = "resource.gif"; // 不存在,資源會加載失敗。
總結
一個設計良好的錯誤處理對編程語言十分重要。JavaScript 提供的 try-cath 和 throw 兩種處理錯誤的方式。但是兩種錯誤處理方式,有不同的錯誤場景要求。如果編寫的庫或函數(shù)需要知道錯誤發(fā)生的具體原因,就拋出異常;如果確切知道接下來的操作,就捕獲錯誤。而沒有通過 try-catch 處理的錯誤,可以使用 window.onerror 事件來處理。
更多內(nèi)容請關注公眾號「海人為記」