本章說明 JavaScript 的例外處理運作方式。它從對例外處理的一般說明開始。
在例外處理中,您經常將緊密結合的陳述分組。如果您在執行這些陳述時,其中一個導致錯誤,那麼繼續執行其餘陳述沒有任何意義。相反,您會盡可能優雅地嘗試從錯誤中復原。這讓人聯想到交易(但沒有原子性)。
讓我們看看沒有例外處理的程式碼
function
processFiles
()
{
var
fileNames
=
collectFileNames
();
var
entries
=
extractAllEntries
(
fileNames
);
processEntries
(
entries
);
}
function
extractAllEntries
(
fileNames
)
{
var
allEntries
=
new
Entries
();
fileNames
.
forEach
(
function
(
fileName
)
{
var
entry
=
extractOneEntry
(
fileName
);
allEntries
.
add
(
entry
);
// (1)
});
}
function
extractOneEntry
(
fileName
)
{
var
file
=
openFile
(
fileName
);
// (2)
...
}
...
對 (2) 中 openFile()
的錯誤做出反應的最佳方式是什麼?顯然,陳述 (1) 不應該再執行。但我們也不想中止 extractAllEntries()
。相反,跳過當前檔案並繼續執行下一個檔案就足夠了。為此,我們將例外處理新增到先前的程式碼
function
extractAllEntries
(
fileNames
)
{
var
allEntries
=
new
Entries
();
fileNames
.
forEach
(
function
(
fileName
)
{
try
{
var
entry
=
extractOneEntry
(
fileName
);
allEntries
.
add
(
entry
);
}
catch
(
exception
)
{
// (2)
errorLog
.
log
(
'Error in '
+
fileName
,
exception
);
}
});
}
function
extractOneEntry
(
fileName
)
{
var
file
=
openFile
(
fileName
);
...
}
function
openFile
(
fileName
)
{
if
(
!
exists
(
fileName
))
{
throw
new
Error
(
'Could not find file '
+
fileName
);
// (1)
}
...
}
例外處理有兩個面向
在 (1) 中,下列建構活躍
processFile() extractAllEntries(...) fileNames.forEach(...) function (fileName) { ... } try { ... } catch (exception) { ... } extractOneEntry(...) openFile(...)
在 (1) 中的 throw
陳述會走上該樹狀結構,並離開所有建構,直到它遇到一個活躍的 try
陳述。然後它會呼叫該陳述的 catch
區塊,並將例外值傳遞給它。
JavaScript 中的例外處理與大多數程式語言中的運作方式類似:一個 try
陳述會將陳述分組,並讓您攔截這些陳述中的例外。
throw
的語法如下:
throw
«
value
»
;
任何 JavaScript 值都可以被擲回。為了簡單起見,許多 JavaScript 程式只擲回字串
// Don't do this
if
(
somethingBadHappened
)
{
throw
'Something bad happened'
;
}
不要這樣做。JavaScript 有用於例外物件的特殊建構函式(請參閱錯誤建構函式)。使用它們或對它們進行子類化(請參閱第 28 章)。它們的優點是,JavaScript 會自動新增堆疊追蹤(在大多數引擎上),而且它們有空間容納其他特定於內容的屬性。最簡單的解決方案是使用內建建構函式 Error()
if
(
somethingBadHappened
)
{
throw
new
Error
(
'Something bad happened'
);
}
try-catch-finally
的語法如下。 try
是強制性的,而且 catch
和 finally
中至少必須有一個:
try
{
«
try_statements
»
}
⟦
catch
(
«
exceptionVar
»
)
{
«
catch_statements
»
}
⟧
⟦
finally
{
«
finally_statements
»
}
⟧
以下是它的運作方式
catch
會捕捉任何在 try_statements
中拋出的 例外狀況,無論是直接拋出或是在它們呼叫的函式中拋出。提示:如果您想要區分不同類型的例外狀況,您可以使用 constructor
屬性來切換例外狀況的建構函式(請參閱 建構函式屬性的使用案例)。
finally
總是會執行,無論在 try_statements
(或是在它們呼叫的函式)中發生什麼事。將它用於清理作業,無論在 try_statements
中發生什麼事,都應該執行這些作業:
var
resource
=
allocateResource
();
try
{
...
}
finally
{
resource
.
deallocate
();
}
如果其中一個 try_statements
是 return
,那麼 finally
區塊會在之後執行(在離開函式或方法之前;請參閱以下範例)。
任何值都可以拋出:
function
throwIt
(
exception
)
{
try
{
throw
exception
;
}
catch
(
e
)
{
console
.
log
(
'Caught: '
+
e
);
}
}
以下是互動方式
> throwIt(3); Caught: 3 > throwIt('hello'); Caught: hello > throwIt(new Error('An error happened')); Caught: Error: An error happened
finally
總是會執行
function
throwsError
()
{
throw
new
Error
(
'Sorry...'
);
}
function
cleansUp
()
{
try
{
throwsError
();
}
finally
{
console
.
log
(
'Performing clean-up'
);
}
}
以下是互動方式
> cleansUp(); Performing clean-up Error: Sorry...
finally
會在 一個 return
陳述式之後執行:
function
idLog
(
x
)
{
try
{
console
.
log
(
x
);
return
'result'
;
}
finally
{
console
.
log
(
"FINALLY"
);
}
}
以下是互動方式
> idLog('arg') arg FINALLY 'result'
回傳值會在執行 finally
之前排隊
var
count
=
0
;
function
countUp
()
{
try
{
return
count
;
}
finally
{
count
++
;
// (1)
}
}
在執行陳述式 (1) 時,count
的值已經排隊準備回傳
> countUp() 0 > count 1
ECMAScript 標準化下列錯誤建構函式。說明摘錄自 ECMAScript 5 規範:
Error
是錯誤的通用建構函式。此處提到的所有其他錯誤建構函式都是子建構函式。
EvalError
「目前未在此規範中使用。此物件保留下來是為了與本規範的先前版本相容。」
RangeError
「表示一個數字值已超過允許範圍。」例如:
> new Array(-1) RangeError: Invalid array length
ReferenceError
「表示已偵測到無效的參考值。」通常,這是一個未知變數。例如:
> unknownVariable ReferenceError: unknownVariable is not defined
SyntaxError
「表示在剖析一般程式碼或剖析 eval()
的引數時發生剖析錯誤」例如:
> 3..1 SyntaxError: Unexpected number '.1'. Parse error. > eval('5 +') SyntaxError: Unexpected end of script
TypeError
「表示運算元的實際類型與預期的類型不同。」例如:
> undefined.foo TypeError: Cannot read property 'foo' of undefined
URIError
「表示以與定義不相容的方式使用其中一個全域 URI 處理函式。」例如:
> decodeURI('%2') URIError: URI malformed
以下是錯誤的屬性:
訊息
名稱
堆疊
通常錯誤的來源可能是外部的(輸入錯誤、檔案遺失等)或內部的(程式中的錯誤)。特別是在後一種情況下,您會遇到意外的例外,需要進行偵錯。通常您沒有執行偵錯器。對於「手動」偵錯,以下兩項資訊很有用:
您可以將第一個項目(資料)的一部分放入例外物件的訊息或屬性中。 第二個項目(執行)在許多 JavaScript 引擎中透過堆疊追蹤獲得支援,這是例外物件建立時呼叫堆疊的快照。以下範例會列印堆疊追蹤:
function
catchIt
()
{
try
{
throwIt
();
}
catch
(
e
)
{
console
.
log
(
e
.
stack
);
// print stack trace
}
}
function
throwIt
()
{
throw
new
Error
(
''
);
}
以下是互動
> catchIt() Error at throwIt (~/examples/throwcatch.js:9:11) at catchIt (~/examples/throwcatch.js:3:9) at repl:1:5