|
|
|
|
|
我們在JS編程時,特別在調(diào)試階段,捕獲錯誤是非常有必要的,這將大大提高我們的勘錯效率。而要把程序編寫得更安全堅固,錯誤處理也必不可少。
JS存在一種語法構(gòu)造try...catch,該語法構(gòu)造使我們可以“捕獲”錯誤,這使腳本可以執(zhí)行得更加合理而不會因遇到“死亡腳本”導(dǎo)致程序執(zhí)行失敗。
js: try-catch-finally捕獲錯誤
“ try…catch”語法
該try...catch構(gòu)造有兩個主要塊:try
和catch
:
try {
// 代碼...
} catch (err) {
// 錯誤處理
}
它是這樣的:
首先,執(zhí)行try {...}
中的代碼。
如果沒有錯誤,則將忽略catch(err)
:執(zhí)行到達try
的結(jié)尾并繼續(xù),跳過catch
。
如果發(fā)生錯誤,則try
執(zhí)行將停止,控制流向catch(err)
的開頭。該err
變量(我們可以使用它的任何名稱)將包含發(fā)生了什么詳細的錯誤對象。
因此,try {...}
塊內(nèi)的錯誤不會殺死腳本,我們有機會在catch
中處理它。
讓我們看一些例子。
一個無錯誤的示例:顯示(1)和(2):
try {
alert('開始運行try'); // (1) <--
// ...這里無錯誤
alert('結(jié)束運行try'); // (2) <--
} catch (err) {
alert('catch被忽略,因為這里無錯誤'); // (3)
}
帶有錯誤的示例:顯示(1)和(3):
try {
alert('開始運行try'); // (1) <--
lalala; // 錯誤,變量未定義!
alert('結(jié)束運行try(永遠不會到達)'); // (2)
} catch (err) {
alert('出現(xiàn)錯誤!'); // (3) <--
}
try...catch 僅適用于運行時錯誤
為了try...catch工作,代碼必須是可運行的。換句話說,它應(yīng)該是有效的JavaScript。
如果代碼在語法上是錯誤的,那么它將無法正常工作,例如,它具有不匹配的花括號:
try {
{{{{{{{{{{{{
} catch (err) {
alert("引擎不懂這個代碼,無效代碼");
}
JavaScript引擎首先讀取代碼,然后運行它。在讀取階段發(fā)生的錯誤稱為“解析時”錯誤,并且無法恢復(fù)(從該代碼內(nèi)部),那是因為引擎無法理解代碼。
因此,try...catch只能處理有效代碼中發(fā)生的錯誤。這種錯誤稱為“運行時錯誤”,有時也稱為“異常”。
try...catch 同步工作
如果在“預(yù)定的”代碼中發(fā)生異常,例如在try
中setTimeout
,則try...catch不會捕獲到該異常:
try {
setTimeout(function() {
noSuchVariable; // 腳本在這里停止
}, 1000);
} catch (err) {
alert( "不會執(zhí)行" );
}
這是因為函數(shù)本身是在引擎已離開try...catch構(gòu)造時稍后執(zhí)行的。
要在預(yù)定函數(shù)內(nèi)捕獲異常,try...catch必須在該函數(shù)內(nèi):
setTimeout(function() {
try {
noSuchVariable; // try...catch 處理這個錯誤!
} catch {
alert( "錯誤被捕獲!" );
}
}, 1000);
try...catch...finally
try...catch構(gòu)造可能還包含一個代碼子句:finally
。
如果存在,它將在所有情況下運行:
擴展語法如下所示:
try {
... 嘗試執(zhí)行這里代碼 ...
} catch (err) {
... 處理錯誤 ...
} finally {
... 總是執(zhí)行 ...
}
try...catch...finally執(zhí)行流程
嘗試運行以下代碼:
try {
alert( 'try' );
if (confirm('出錯?')) BAD_CODE();
} catch (err) {
alert( 'catch' );
} finally {
alert( 'finally' );
}
該代碼有兩種執(zhí)行方式:
finally 和 return
該finally
子句適用于try...catch的任何退出,這里包括一個顯式的return
。
在下面的示例中,有一個returnin try。在這種情況下,finally
在控件返回外部代碼之前執(zhí)行。
function func() {
try {
return 1;
} catch (err) {
/* ... */
} finally {
alert( 'finally' );
}
}
alert( func() ); // 首先執(zhí)行finally里的alert, 然后再執(zhí)行try
try...finally
try...finally不帶catch
子句的構(gòu)造也很有用。當(dāng)我們不想在這里處理錯誤時,我們可以應(yīng)用它,但是要確保我們啟動的過程已經(jīng)完成。
function func() {
// 開始做一些需要完成的事情
try {
// ...
} finally {
// 即使全部死亡都完成那件事
}
}
try...catch有無finally的區(qū)別
比較兩個代碼片段。
1、第一個用于finally
執(zhí)行以下代碼try...catch:
try {
// 工作 工作
} catch (err) {
// 處理錯誤
} finally {
清洗工作空間
}
2、第二個片段把清洗空間放在try...catch的后面:
try {
// 工作 工作
} catch (err) {
// 處理錯誤
}
清洗工作空間
我們肯定需要在工作后進行清理,無論是否有錯誤都沒有關(guān)系。
兩個代碼片段是否相等?在這里使用finally
是否有優(yōu)勢?下面兩個例子說明了使用finally
的優(yōu)勢。
當(dāng)我們看一個函數(shù)內(nèi)部的代碼時,區(qū)別變得很明顯。
如果“跳出”,則try...catch的行為會有所不同。
例如,當(dāng)有一個return
在try...catch內(nèi)部時,該finally
在任何來自try...catch的出口工作,甚至是通過return
聲明。catch
將在執(zhí)行完finally
代碼之后再執(zhí)行。
function f() {
try {
alert('開始');
return "結(jié)果";
} catch (err) {
/// ...
} finally {
alert('清洗!');
}
}
f(); // 清洗!
又例如,當(dāng)函數(shù)內(nèi)有throw
時:
function f() {
try {
alert('開始');
throw new Error("一個錯誤");
} catch (err) {
// ...
if("can't handle the error") {
throw err;
}
} finally {
alert('清洗!')
}
}
f(); // 清洗!
這finally
是保證在這里進行清理。如果僅將代碼放在的f()
末尾,則在這些情況下將無法運行該代碼。