毋庸置疑的是,在程序設計中,錯誤處理是很重要的一個環(huán)節(jié)。不管水平多高的軟件開發(fā)人員,都或多或少地難以避免寫出邏輯不是特別嚴謹?shù)拇a。另一方面來說,當我們的寫的代碼中帶有錯誤處理的邏輯時,當出現(xiàn)了錯誤時可以及時通知到用戶,這會帶來用戶體驗上的提升。作為開發(fā)人員,我們要知道如何處理Javascript錯誤。
try-catch語句
在Javascript中,通過try-catch語句來實現(xiàn)異常的處理。該語句作為標準的一部分,其語法規(guī)則如下:
`try {`
`.....`
`} catch (error) {`
`.....`
`}`
在使用時,我們可以將所有可能會拋出錯誤的代碼放在try語句塊中,在catch語句塊中是對錯誤的處理。 當try塊中出現(xiàn)錯誤時,就會退出try塊,從而執(zhí)行catch中的錯誤處理代碼。在上面的語法規(guī)則中,可以發(fā)現(xiàn)catch接受一個錯誤對象error.在error中主要存在兩個屬性:name屬性 和 message屬性。
這里要注意的是,如果在try塊中執(zhí)行代碼時遇到錯誤,出現(xiàn)錯誤的語句后面的語句都不會再被執(zhí)行。如果想要不管有沒有遇到錯誤都執(zhí)行一些語句,那么可以把這些語句放在finally子句中。
finally子句
標準中還引入了另一個子句:finally語句。它是搭配try-catch語句的一個可選的語句。但是,一旦使用,無論try-catch語句塊中包含什么代碼,finally中的代碼都會執(zhí)行。
try中的代碼正常執(zhí)行,finally中的語句正常執(zhí)行。
try中的代碼執(zhí)行過程中出錯從而執(zhí)行catch語句, finally中的語句正常執(zhí)行。
try塊中存在return語句,finally中的代碼仍然正常執(zhí)行。(只要包含finally語句,try-catch中的return都將被忽略!)
Javascript中的錯誤類型
- Error
基類型。其他的錯誤類型均繼承自它。這個基類型的主要目的是供開發(fā)人員拋出自定義錯誤。
- EvalError
由于使用eval函數(shù)引發(fā)的錯誤。
- SyntaxError
語法錯誤。
- TypeError
類型錯誤。當錯誤使用變量或對象時,會拋出該錯誤。
- ReferenceError
引用錯誤。當引用一個不存在的對象或不存在的變量時會發(fā)生該錯誤。
- RangeError
范圍錯誤。數(shù)值超出范圍時觸發(fā)。比如一個數(shù)組元素的取值為負值,就會拋出該錯誤。
- URIError
合理使用try-catch
當我們使用try-catch處理錯誤之后,瀏覽器就不會再對錯誤進行處理。瀏覽器如何處理錯誤,下面會講到。使用try-catch的情況一般為try塊中的代碼是我們無法控制的,也就是說我們不能確定它會不會出現(xiàn)錯誤,如果出現(xiàn)錯誤,是哪種錯誤。 比如,我們使用了第三方庫或是別人寫的工具函數(shù)之類的,我們無法確定這些函數(shù)會不會有意無意地拋出一些錯誤。因此,在這種情況下使用try-catch來對使用的函數(shù)進行包裹,從而對可能出現(xiàn)的錯誤進行處理。
當我們明確知道代碼會發(fā)生錯誤時,再使用try-catch就不是太合適了。而且,在上面我們也介紹了,Error對象是基類型,主要目的是用來拋出自定義錯誤。
Throw
在JS中,我們通過throw來拋出錯誤。而且,throw的使用情況為: 我們明確知道代碼會發(fā)生錯誤的情況。 比如:
`function test(arr) {`
`// 此處應該傳進來一個數(shù)組`
`alert(arr[0])`
`}`
對于這種情況,我們需要對參數(shù)arr進行一個處理。可以發(fā)現(xiàn),我們在test函數(shù)里面,默認出進來的參數(shù)為數(shù)組,從而直接進行數(shù)組操作。但是如果傳進來的不是數(shù)組呢? 肯定會出錯。這就需要我們對參數(shù)進行一個判斷,如果不是數(shù)組,則拋出一個錯誤。
`function test(arr) {`
`if (!Array.isArray(arr))`
`throw new Error("test(): arr must be an Array instance ")`
` alert(arr[0])`
`}`
- 通過throw拋出的錯誤會被外層的catch語句塊所捕獲。如果沒有catch語句塊,則會被瀏覽器捕獲,從而在瀏覽器控制臺可以看到錯誤信息。
這里要注意,拋出的錯誤類型不一定非要是Error,也可以是上面所說的任何類型,甚至是自定義錯誤類型。
對于自定義錯誤類型,可以通過繼承自Error來實現(xiàn)。
如何自定義錯誤類型
其實,我們可以不一定通過繼承Error來實現(xiàn)自定義錯誤類型。
image
- 使用繼承的好處就是可以通過obj instanceof Error 來識別這個一個錯誤對象。
`class MyError extends Error {`
`constructor(message) {`
`super(message);`
`this.name = "MyError"`
`}`
`}`
這樣的話,我們就可以在catch中對其進行判斷。
`try {`
`.....`
`} catch (error) {`
`if (error instanceof Error) {`
` .................`
` }`
`}`
`// 更多詳細的內(nèi)容建議參考: https://zh.javascript.info/custom-errors`
我們還可以在catch中通過throw來拋出錯誤。這種情況發(fā)生在: 我們在catch中對收到的error做一個篩選,如果是特定的錯誤,對其進行處理。否則,表明出現(xiàn)了未知錯誤,將它繼續(xù)拋出去。
錯誤對象(error)
上面說了,catch語句會收到關于錯誤信息的error對象。該對象主要有以下兩種屬性:
Name 錯誤類型名稱
Message 關于error的描述
還有其他的一些屬性,根據(jù)瀏覽器的不同而有所不同。不過,最廣泛使用和支持的是:
- Stack 當前調用棧信息
Throw 與try-catch的說明
捕獲錯誤是為了防止瀏覽器對其進行默認處理。拋出錯誤是為了提供錯誤發(fā)生的具體原因信息。
error事件
當代碼中出現(xiàn)錯誤,而未使用try-catch進行捕獲處理的情況下,瀏覽器會捕獲到該錯誤。即: 任何沒有通過try-catch處理的錯誤都會觸發(fā)window的error事件。在任何瀏覽器中,onerror事件處理程序都不會接受一個event對象,相反,接受的是三個參數(shù): message(錯誤信息), url(錯誤所在的URL) 和 line(行號)。
`window.onerror = function ( message, url, line ) {`
`........`
`}`
這里要注意的是,onerror事件只能通過上面的方式添加事件處理程序,不可以通過DOM2的addEventListener。
在事件處理程序中,通過return false可以阻止瀏覽器報告錯誤的默認行為。
`window.onerror = function (message, url, line ) {`
`console.log(message)`
`return false;`
`}`
圖像也支持error事件。只要圖像的src指定的URL返回的圖像格式不可被識別,就會觸發(fā)error事件。但是他的事件處理程序會接收event對象。
常見錯誤類型
數(shù)據(jù)類型錯誤
其實就是指我們在編寫代碼的過程前沒有確保使用的變量和函數(shù)參數(shù)的數(shù)據(jù)類型的正確性。因為JS是松散類型的,因此開發(fā)人員要編寫適當?shù)臄?shù)據(jù)類型檢測代碼。要注意的一點是,當進行類型判斷時,如果是基本類型,應該使用typeof來檢測,而對象的值則應該使用instanceof來進行檢測。
類型轉換錯誤
這種錯誤比較常見的是== , === 和 if , while , for這些控制語句中的布爾條件判斷。舉一個常見的例子:
`function test(arr, arr2) {`
`if (!arr2) {`
`return arr;`
`}`
`}`
- 我們想的是,如果不存在arr2,就返回arr。如果arr2為undefined,滿足條件。如果arr2存在,且為0的話,仍然會以arr2不存在進行處理。顯然,這里就出現(xiàn)了邏輯上的錯誤。因此,要加上合適的判斷。
`if ( typeof arr2 === 'undefined' ) return arr`
通信錯誤
格式不正確的URL有關。
服務器處理出現(xiàn)的錯誤。
錯誤上傳服務器
很多時候,我們都需要把錯誤信息上傳給服務器保存。那么,怎么上傳就是一個問題。
正如上面所說,Image對象也會觸發(fā)error事件,那么我們可以使用image的error事件。主要由以下幾個好處:
支持度高。幾乎所有的瀏覽器都支持Image對象。
可以避免跨域限制。
記錄錯誤的過程中出問題的概率較低。