js 系統教程-17-js 語法之異常處理-error,自定義異常,try catch finally
目錄
錯誤處理機制
Error 例項物件
JavaScript 解析或執行時,一旦發生錯誤,引擎就會丟擲一個錯誤物件。JavaScript 原生提供Error建構函式,所有丟擲的錯誤都是這個建構函式的例項。
var err = new Error('出錯了');
err.message // "出錯了"
JavaScript 語言標準只提到,Error例項物件必須有message屬性,表示出錯時的提示資訊,沒有提到其他屬性。大多數 JavaScript 引擎,對Error例項還提供name和stack屬性,分別表示錯誤的名稱和錯誤的堆疊,但它們是非標準的,不是每種實現都有。
message:錯誤提示資訊
name:錯誤名稱(非標準屬性)
stack:錯誤的堆疊(非標準屬性)
原生錯誤型別
Error
例項物件是最一般的錯誤型別,在它的基礎上,JavaScript 還定義了其他6種錯誤物件。也就是說,存在Error的6個派生物件。
SyntaxError 物件
SyntaxError物件是解析程式碼時發生的語法錯誤。
// 變數名錯誤
var 1a;
// Uncaught SyntaxError: Invalid or unexpected token
// 缺少括號
console.log 'hello');
// Uncaught SyntaxError: Unexpected string
上面程式碼的錯誤,都是在語法解析階段就可以發現,所以會丟擲SyntaxError。第一個錯誤提示是“token 非法”,第二個錯誤提示是“字串不符合要求”。
ReferenceError 物件
ReferenceError 物件是引用一個不存在的變數時發生的錯誤。
// 使用一個不存在的變數
unknownVariable
// Uncaught ReferenceError: unknownVariable is not defined
另一種觸發場景是,將一個值分配給無法分配的物件,比如對函式的執行結果或者this賦值。
// 等號左側不是變數
console.log() = 1
// Uncaught ReferenceError: Invalid left-hand side in assignment
// this 物件不能手動賦值
this = 1
// ReferenceError: Invalid left-hand side in assignment
上面程式碼對函式console.log的執行結果和this賦值,結果都引發了ReferenceError錯誤。
RangeError 物件
RangeError 物件是一個值超出有效範圍時發生的錯誤。主要有幾種情況,一是陣列長度為負數,二是Number物件的方法引數超出範圍,以及函式堆疊超過最大值。
// 陣列長度不得為負數
new Array(-1)
// Uncaught RangeError: Invalid array length
TypeError 物件
TypeError 物件是變數或引數不是預期型別時發生的錯誤。比如,對字串、布林值、數值等原始型別的值使用new命令,就會丟擲這種錯誤,因為new命令的引數應該是一個建構函式。
new 123
// Uncaught TypeError: number is not a func
var obj = {};
obj.unknownMethod()
// Uncaught TypeError: obj.unknownMethod is not a function
上面程式碼的第二種情況,呼叫物件不存在的方法,也會丟擲TypeError錯誤,因為obj.unknownMethod的值是undefined,而不是一個函式。
URIError 物件
URIError 物件是 URI 相關函式的引數不正確時丟擲的錯誤,
主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()這六個函式。
decodeURI('%2')
// URIError: URI malformed
EvalError 物件
eval函式沒有被正確執行時,會丟擲EvalError錯誤。該錯誤型別已經不再使用了,只是為了保證與以前程式碼相容,才繼續保留。
自定義錯誤
除了 JavaScript 原生提供的七種錯誤物件,還可以定義自己的錯誤物件。
function UserError(message) {
this.message = message || '預設資訊';
this.name = 'UserError';
}
UserError.prototype = new Error();
UserError.prototype.constructor = UserError;
上面程式碼自定義一個錯誤物件UserError,讓它繼承Error物件。然後,就可以生成這種自定義型別的錯誤了。
new UserError('這是自定義的錯誤!');
throw 語句
throw語句的作用是手動中斷程式執行,丟擲一個錯誤。
if (x < 0) {
throw new Error('x 必須為正數');
}
// Uncaught ReferenceError: x is not defined
上面程式碼中,如果變數x小於0,就手動丟擲一個錯誤,告訴使用者x的值不正確,整個程式就會在這裡中斷執行。
可以看到,throw丟擲的錯誤就是它的引數,這裡是一個Error例項。
throw也可以丟擲自定義錯誤。
function UserError(message) {
this.message = message || '預設資訊';
this.name = 'UserError';
}
throw new UserError('出錯了!');
// Uncaught UserError {message: "出錯了!", name: "UserError"}
上面程式碼中,throw丟擲的是一個UserError例項。
實際上,throw可以丟擲任何型別的值。也就是說,它的引數可以是任何值。
// 丟擲一個字串
throw 'Error!';
// Uncaught Error!
// 丟擲一個數值
throw 42;
// Uncaught 42
// 丟擲一個布林值
throw true;
// Uncaught true
// 丟擲一個物件
throw {
toString: function () {
return 'Error!';
}
};
// Uncaught {toString: ƒ}
對於 JavaScript 引擎來說,遇到throw語句,程式就中止了。
引擎會接收到throw丟擲的資訊,可能是一個錯誤例項,也可能是其他型別的值。
try…catch 結構
一旦發生錯誤,程式就中止執行了。JavaScript 提供了try…catch結構,允許對錯誤進行處理,選擇是否往下執行。
try {
throw new Error('出錯了!');
} catch (e) {
console.log(e.name + ": " + e.message);
console.log(e.stack);
}
// Error: 出錯了!
// at <anonymous>:3:9
// ...
上面程式碼中,try程式碼塊丟擲錯誤(上例用的是throw語句),JavaScript 引擎就立即把程式碼的執行,轉到catch程式碼塊,或者說錯誤被catch程式碼塊捕獲了。catch接受一個引數,表示try程式碼塊丟擲的值。
如果你不確定某些程式碼是否會報錯,就可以把它們放在try…catch程式碼塊之中,便於進一步對錯誤進行處理。
try {
f();
} catch(e) {
// 處理錯誤
}
上面程式碼中,如果函式f執行報錯,就會進行catch程式碼塊,接著對錯誤進行處理。
catch程式碼塊捕獲錯誤之後,程式不會中斷,會按照正常流程繼續執行下去。
try {
throw "出錯了";
} catch (e) {
console.log(111);
}
console.log(222);
// 111
// 222
上面程式碼中,try程式碼塊丟擲的錯誤,被catch程式碼塊捕獲後,程式會繼續向下執行。
catch程式碼塊之中,還可以再丟擲錯誤,甚至使用巢狀的try…catch結構。
var n = 100;
try {
throw n;
} catch (e) {
if (e <= 50) {
// ...
} else {
throw e;
}
}
// Uncaught 100
上面程式碼中,catch程式碼之中又丟擲了一個錯誤。
為了捕捉不同型別的錯誤,catch程式碼塊之中可以加入判斷語句。
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
console.log(e.name + ": " + e.message);
} else if (e instanceof RangeError) {
console.log(e.name + ": " + e.message);
}
// ...
}
上面程式碼中,catch捕獲錯誤之後,會判斷錯誤型別(EvalError還是RangeError),進行不同的處理。
finally 程式碼塊
try…catch結構允許在最後新增一個finally程式碼塊,表示不管是否出現錯誤,都必需在最後執行的語句。
function cleansUp() {
try {
throw new Error('出錯了……');
console.log('此行不會執行');
} finally {
console.log('完成清理工作');
}
}
cleansUp()
// 完成清理工作
// Error: 出錯了……
上面程式碼中,由於沒有catch語句塊,所以錯誤沒有捕獲。執行finally程式碼塊以後,程式就中斷在錯誤丟擲的地方。
function idle(x) {
try {
console.log(x);
return 'result';
} finally {
console.log("FINALLY");
}
}
idle('hello')
// hello
// FINALLY
// "result"