錯誤處理機制
一、Error實例對象
- JavaScript 解析或運行時,一旦發生錯誤,引擎就會拋出一個錯誤對象。JavaScript 原生提供Error構造函數,所有拋出的錯誤都是這個構造函數的實例。
-
Error
實例對象必須有message
屬性,表示出錯時的提示信息。Error
實例還提供name
和stack
屬性,分別表示錯誤的名稱和錯誤的堆棧。但它們是非標準的,不是每種實現都有。
message
:錯誤提示信息
name
:錯誤名稱(非標準屬性)
stack
:錯誤的堆棧(非標準屬性)
Error的六個派生對象:
SyntaxError
對象(語法錯誤)
SyntaxError
對象是解析代碼時發生的語法錯誤。ReferenceError
對象(引用錯誤)
ReferenceError
對象是引用一個不存在的變量時發生的錯誤。
例如:將一個值分配給無法分配的對象,比如對函數的運行結果或者this
賦值。RangeError
對象(引用錯誤)
RangeError
對象是一個值超出有效范圍時發生的錯誤
主要有幾種情況,一是數組長度為負數,二是Number
對象的方法參數超出范圍,以及函數堆棧超過最大值。TypeError
對象(類型錯誤)
TypeError
對象是變量或參數不是預期類型時發生的錯誤
例如: 對字符串、布爾值、數值等原始類型的值使用new命令URIError
對象(編碼錯誤)
URIError
對象是URI
相關函數的參數不正確時拋出的錯誤EvalError
對象(全局錯誤)
eval函數沒有被正確執行時,會拋出EvalError錯誤。該錯誤類型已經不再使用了,只是為了保證與以前代碼兼容,才繼續保留。
以上這6種派生錯誤,連同原始的Error對象,都是構造函數。開發者可以使用它們,手動生成錯誤對象的實例。這些構造函數都接受一個參數,代表錯誤提示信息(message)。
二、throw 語句
throw
語句的作用是手動中斷程序執行,拋出一個錯誤。
if (x <= 0) {
throw new Error('x 必須為正數');
}
// Uncaught ReferenceError: x is not defined
throw
拋出的錯誤就是它的參數,這里是一個Error實例。
throw
可以拋出任何類型的值。也就是說,它的參數可以是任何值。
// 拋出一個字符串
throw 'Error!';
// Uncaught Error!
// 拋出一個數值
throw 42;
// Uncaught 42
// 拋出一個布爾值
throw true;
// Uncaught true
// 拋出一個對象
throw {
toString: function () {
return 'Error!';
}
};
// Uncaught {toString: ?}
對于 JavaScript 引擎來說,遇到throw語句,程序就中止了
三、try...catch結構
- 一旦發生錯誤,程序就中止執行了。JavaScript 提供了
try...catch
結構,允許對錯誤進行處理,選擇是否往下執行。 - try語句包含了由一個或者多個語句組成的try塊, 和至少一個
catch
子句或者一個finally
子句的其中一個,或者兩個兼有。 - 三種形式的
try
聲明:
1.try...catch
try...finally
try...catch...finally
try {
throw new Error('出錯了!');
} catch (e) {
console.log(e.name + ": " + e.message);
console.log(e.stack);
}
// Error: 出錯了!
// at <anonymous>:3:9
// ...
-
catch
子句包含try
塊中拋出異常時要執行的語句。即:想讓try
語句中的內容成功, 如果沒成功,想控制接下來發生的事情,這時可以在catch
語句中實現。 如果在try
塊中有任何一個語句(或者從try
塊中調用的函數)拋出異常,控制立即轉向catch
子句。如果在try
塊中沒有異常拋出,會跳過catch
子句。 -
catch
代碼塊捕獲錯誤之后,程序不會中斷,會按照正常流程繼續執行下去。 -
catch
代碼塊之中,還可以再拋出錯誤,甚至使用嵌套的try...catch
結構。
var n = 100;
try {
throw n;
} catch (e) {
if (e <= 50) {
// ...
} else {
throw e;
}
}
// Uncaught 100
-
catch
接受一個參數,表示try
代碼塊拋出的值。 -
catch
代碼塊之中可以加入判斷語句if...else
。用于判斷錯誤類型,進行不同的處理。
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);
}
// ...
}
-
finally
子句在try
塊和catch
塊之后執行但是在下一個try
聲明之前執行。無論是否有異常拋出或捕獲它總是執行。
四、finally 代碼塊
-
ry...catch
結構允許在最后添加一個finally
代碼塊,表示不管是否出現錯誤,都必需在最后運行的語句。
function cleansUp() {
try {
throw new Error('出錯了……');
console.log('此行不會執行');
} finally {
console.log('完成清理工作');
}
}
cleansUp()
// 完成清理工作
// Uncaught Error: 出錯了……
// at cleansUp (<anonymous>:3:11)
// at <anonymous>:10:1
上面代碼中,由于沒有catch
語句塊,一旦發生錯誤,代碼就會中斷執行。中斷執行之前,會先執行finally
代碼塊,然后再向用戶提示報錯信息。
function idle(x) {
try {
console.log(x);
return 'result';
} finally {
console.log('FINALLY');
}
}
idle('hello')
// hello
// FINALLY
上面代碼中,try
代碼塊沒有發生錯誤,而且里面還包括return語句,但是finally
代碼塊依然會執行。而且,這個函數的返回值還是result
。
-
return
語句的執行是排在finally
代碼之前,只是等finally
代碼執行完畢后才返回。 - 下面是finally代碼塊用法的典型場景。
openFile();
try {
writeFile(Data);
} catch(e) {
handleError(e);
} finally {
closeFile();
}
上面代碼首先打開一個文件,然后在try
代碼塊中寫入文件,如果沒有發生錯誤,則運行finally
代碼塊關閉文件;一旦發生錯誤,則先使用catch
代碼塊處理錯誤,再使用finally
代碼塊關閉文件。
-
try...catch...finally
這三者之間的執行順序,例:
function f() {
try {
console.log(0);
throw 'bug';
} catch(e) {
console.log(1);
return true; // 這句原本會延遲到 finally 代碼塊結束再執行
console.log(2); // 不會運行
} finally {
console.log(3);
return false; // 這句會覆蓋掉前面那句 return
console.log(4); // 不會運行
}
console.log(5); // 不會運行
}
var result = f();
// 0
// 1
// 3
result
// false
上面例子中catch
代碼塊結束執行之前,會先執行finally
代碼塊。