當JavaScript 引擎執行 JavaScript 代碼時,會發生各種錯誤:可能是語法錯誤,通常是程序員造成的編碼錯誤或錯別字;可能是拼寫錯誤或語言中缺少的功能(可能由於瀏覽器差異);可能是由於來自服務器或用戶的錯誤輸出而導致的錯誤;也可能是由於許多其他不可預知的因素。那麼js有沒有好的可以檢測錯誤的呢?
JS本身是動態語言,因此人們普遍認為它是一種最難於調試的編程語言。腳本出錯時,由於沒有上下文信息,讓人摸不著頭腦,ECMAScript 第 3 版致力於解決這個問題,專門引入了try-catch和throw等語句以及一些錯誤類型,意在讓開發人員能夠適當地處理錯誤,提升開發效率。
1. Error 實例對象
JavaScript 解析或運行時,一旦發生錯誤,引擎就會拋出一個錯誤對象。JavaScript 原生提供Error構造函數,所有拋出的錯誤都是這個構造函數的實例。
<code>var
err =new
Error
('出錯了'
); err.message /<code>
拋出Error實例對象以後,整個程序就中斷在發生錯誤的地方,不再往下執行。
Error具有下面一些主要屬性:
- description: 錯誤描述 (僅IE可用).
- fileName: 出錯的文件名 (僅Mozilla可用).
- lineNumber: 出錯的行數 (僅Mozilla可用).
- message: 錯誤信息 (在IE下同description)
- name: 錯誤類型.
- number: 錯誤代碼 (僅IE可用).
- stack: 像Java中的Stack Trace一樣的錯誤堆棧信息 (僅Mozilla可用).
2. 原生錯誤類型
Error 實例對象是最一般的錯誤類型,在它的基礎上,JavaScript 還定義了其他 6 種錯誤對象。也就是說,存在Error 的 6 個派生對象。
2.1 SyntaxError 對象
SyntaxError 對象是解析代碼時發生的語法錯誤。
<code>var
1
a;console
.log'hello'
); /<code>
2.2 ReferenceError 對象
ReferenceError 對象是引用一個不存在的變量時發生的錯誤。
<code> unknownVariable /<code>
另一種觸發場景是,將一個值分配給無法分配的對象。
<code> console.log
() =1
this
=1
/<code>
2.3 RangeError 對象
RangeError 對象是一個值超出有效範圍時發生的錯誤。主要有幾種情況,一是數組長度為負數,二是 Number 對象的方法參數超出範圍,以及函數堆棧超過最大值。
<code>new
Array
(-1
) /<code>
2.4 TypeError 對象
TypeError 對象是變量或參數不是預期類型時發生的錯誤。比如,對字符串、布爾值、數值等原始類型的值使用 new 命令,就會拋出這種錯誤,因為 new 命令的參數應該是一個構造函數。
<code>new
123
var
obj = {}; obj.unknownMethod() /<code>
2.5 URIError 對象
URIError 對象是 URI 相關函數的參數不正確時拋出的錯誤,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()這六個函數。
2.6 EvalError 對象
eval函數沒有被正確執行時,會拋出EvalError錯誤。該錯誤類型已經不再使用了,只是為了保證與以前代碼兼容,才繼續保留。
2.7 總結
以上這6種派生錯誤,連同原始的 Error 對象,都是構造函數。這些構造函數都接受一個參數,代表錯誤提示信息(message)。
<code>var
err1 =new
Error
('出錯了!'
);var
err2 =new
RangeError
('出錯了,變量超出有效範圍!'
);var
err3 =new
TypeError
('出錯了,變量類型無效!'
); err1.message err2.message err3.message /<code>
3. throw 語句
throw 語句的作用是主動中斷程序執行,拋出一個錯誤。
<code>var
x =-1
if
(x <=0
) {throw
new
Error
('x 必須為正數'
); } /<code>
上面代碼中,如果變量 x 小於等於 0,就手動拋出一個錯誤,告訴用戶 x 的值不正確,整個程序就會在這裡中斷執行。可以看到,throw 拋出的錯誤就是它的參數,這裡是一個 Error 實例。
throw 可以拋出任何類型的值。也就是說,它的參數可以是任何值。
<code>throw
'Error!'
;throw
42
; /<code>
對於 JavaScript 引擎來說,遇到throw 語句,程序就中止了。引擎會接收到throw拋出的信息,可能是一個錯誤實例,也可能是其他類型的值。
4. try...catch 結構
一旦發生錯誤,程序就中止執行了。JavaScript 提供了 try...catch 結構,允許對錯誤進行處理,選擇是否往下執行。
如果你不確定某些代碼是否會報錯,就可以把它們放在 try...catch 代碼塊之中,便於進一步對錯誤進行處理。
<code>try
{throw
"出錯了"
; }catch
(e) { console.log
(111
); } console.log
(222
); /<code>
上面代碼中,try 代碼塊拋出的錯誤,被catch代碼塊捕獲後,程序不會中斷,會按照正常流程繼續執行下去。
5. finally 代碼塊
try...catch 結構允許在最後添加一個finally代碼塊,表示不管是否出現錯誤,都必需在最後運行的語句。
<code>function
cleansUp
() {try
{throw
new
Error
('出錯了……'
);console
.log('此行不會執行'
); }finally
{console
.log('完成清理工作'
); } } cleansUp() /<code>
上面代碼中,由於沒有 catch 語句塊,一旦發生錯誤,代碼就會中斷執行。中斷執行之前,會先執行 finally 代碼塊,然後再向用戶提示報錯信息。
下面的例子說明,return 語句的執行是排在finally代碼之前,只是等finally代碼執行完畢後才返回。
<code>var
count =0
;function
countUp
() {try
{return
count; }finally
{ count++; } } countUp() count /<code>
下面的例子充分反映了 try...catch...finally 這三者之間的執行順序。
<code>function
f
() {try
{console
.log(0
);throw
'bug'
; }catch
(e) {console
.log(1
);return
true
;console
.log(2
); }finally
{console
.log(3
);return
false
;console
.log(4
); }console
.log(5
); }var
result = f(); result /<code>
上面代碼中,catch 代碼塊結束執行之前,會先執行 finally 代碼塊。
finally代碼塊用法的典型場景:
<code>openFile
();try
{writeFile
(Data); }catch
(e) {handleError
(e); }finally
{closeFile
(); } /<code>
上面代碼首先打開一個文件,然後在 try 代碼塊中寫入文件,如果沒有發生錯誤,則運行 finally 代碼塊關閉文件;一旦發生錯誤,則先使用 catch 代碼塊處理錯誤,再使用 finally 代碼塊關閉文件。