|
|
|
|
|
|
我們在JS編程時,特別在調試階段,捕獲錯誤是非常有必要的,這將大大提高我們的勘錯效率。而要把程序編寫得更安全堅固,錯誤處理也必不可少。
JS存在一種語法構造try...catch,該語法構造使我們可以“捕獲”錯誤,這使腳本可以執行得更加合理而不會因遇到“死亡腳本”導致程序執行失敗。

js: try-catch-finally捕獲錯誤
“ try…catch”語法
該try...catch構造有兩個主要塊:try和catch:
try {
// 代碼...
} catch (err) {
// 錯誤處理
}
它是這樣的:
首先,執行try {...}中的代碼。
如果沒有錯誤,則將忽略catch(err):執行到達try的結尾并繼續,跳過catch。
如果發生錯誤,則try執行將停止,控制流向catch(err)的開頭。該err變量(我們可以使用它的任何名稱)將包含發生了什么詳細的錯誤對象。
因此,try {...}塊內的錯誤不會殺死腳本,我們有機會在catch中處理它。
讓我們看一些例子。
一個無錯誤的示例:顯示(1)和(2):
try {
alert('開始運行try'); // (1) <--
// ...這里無錯誤
alert('結束運行try'); // (2) <--
} catch (err) {
alert('catch被忽略,因為這里無錯誤'); // (3)
}
帶有錯誤的示例:顯示(1)和(3):
try {
alert('開始運行try'); // (1) <--
lalala; // 錯誤,變量未定義!
alert('結束運行try(永遠不會到達)'); // (2)
} catch (err) {
alert('出現錯誤!'); // (3) <--
}
try...catch 僅適用于運行時錯誤
為了try...catch工作,代碼必須是可運行的。換句話說,它應該是有效的JavaScript。
如果代碼在語法上是錯誤的,那么它將無法正常工作,例如,它具有不匹配的花括號:
try {
{{{{{{{{{{{{
} catch (err) {
alert("引擎不懂這個代碼,無效代碼");
}
JavaScript引擎首先讀取代碼,然后運行它。在讀取階段發生的錯誤稱為“解析時”錯誤,并且無法恢復(從該代碼內部),那是因為引擎無法理解代碼。
因此,try...catch只能處理有效代碼中發生的錯誤。這種錯誤稱為“運行時錯誤”,有時也稱為“異常”。
try...catch 同步工作
如果在“預定的”代碼中發生異常,例如在try中setTimeout,則try...catch不會捕獲到該異常:
try {
setTimeout(function() {
noSuchVariable; // 腳本在這里停止
}, 1000);
} catch (err) {
alert( "不會執行" );
}
這是因為函數本身是在引擎已離開try...catch構造時稍后執行的。
要在預定函數內捕獲異常,try...catch必須在該函數內:
setTimeout(function() {
try {
noSuchVariable; // try...catch 處理這個錯誤!
} catch {
alert( "錯誤被捕獲!" );
}
}, 1000);
try...catch...finally
try...catch構造可能還包含一個代碼子句:finally。
如果存在,它將在所有情況下運行:
擴展語法如下所示:
try {
... 嘗試執行這里代碼 ...
} catch (err) {
... 處理錯誤 ...
} finally {
... 總是執行 ...
}

try...catch...finally執行流程
嘗試運行以下代碼:
try {
alert( 'try' );
if (confirm('出錯?')) BAD_CODE();
} catch (err) {
alert( 'catch' );
} finally {
alert( 'finally' );
}
該代碼有兩種執行方式:
finally 和 return
該finally子句適用于try...catch的任何退出,這里包括一個顯式的return。
在下面的示例中,有一個returnin try。在這種情況下,finally在控件返回外部代碼之前執行。
function func() {
try {
return 1;
} catch (err) {
/* ... */
} finally {
alert( 'finally' );
}
}
alert( func() ); // 首先執行finally里的alert, 然后再執行try
try...finally
try...finally不帶catch子句的構造也很有用。當我們不想在這里處理錯誤時,我們可以應用它,但是要確保我們啟動的過程已經完成。
function func() {
// 開始做一些需要完成的事情
try {
// ...
} finally {
// 即使全部死亡都完成那件事
}
}
try...catch有無finally的區別
比較兩個代碼片段。
1、第一個用于finally執行以下代碼try...catch:
try {
// 工作 工作
} catch (err) {
// 處理錯誤
} finally {
清洗工作空間
}
2、第二個片段把清洗空間放在try...catch的后面:
try {
// 工作 工作
} catch (err) {
// 處理錯誤
}
清洗工作空間
我們肯定需要在工作后進行清理,無論是否有錯誤都沒有關系。
兩個代碼片段是否相等?在這里使用finally是否有優勢?下面兩個例子說明了使用finally的優勢。
當我們看一個函數內部的代碼時,區別變得很明顯。
如果“跳出”,則try...catch的行為會有所不同。
例如,當有一個return在try...catch內部時,該finally在任何來自try...catch的出口工作,甚至是通過return聲明。catch將在執行完finally代碼之后再執行。
function f() {
try {
alert('開始');
return "結果";
} catch (err) {
/// ...
} finally {
alert('清洗!');
}
}
f(); // 清洗!
又例如,當函數內有throw時:
function f() {
try {
alert('開始');
throw new Error("一個錯誤");
} catch (err) {
// ...
if("can't handle the error") {
throw err;
}
} finally {
alert('清洗!')
}
}
f(); // 清洗!
這finally是保證在這里進行清理。如果僅將代碼放在的f()末尾,則在這些情況下將無法運行該代碼。
