從瀏覽器的控制臺(tái)到運(yùn)行Node.js的計(jì)算機(jī)終端,我們到處都會(huì)看到各類錯(cuò)誤。
這篇文章的重點(diǎn)是概述我們?cè)贘S開發(fā)過程中可能遇到的錯(cuò)誤類型。
1. RangeError
當(dāng)數(shù)字超出允許的值范圍時(shí),將拋出此錯(cuò)誤。例如:
我們有一個(gè)帶有兩個(gè)元素的arr。接下來,我們嘗試使數(shù)組包含90**99 == 2.9512665430652753e+193元素。
這個(gè)數(shù)字超出了大小數(shù)組可以增長(zhǎng)的范圍。所以運(yùn)行時(shí)它會(huì)拋出RangeError:
因?yàn)槲覀円黾觓rr數(shù)組的數(shù)量超出了JS指定的范圍。
2. ReferenceError
當(dāng)對(duì)變量/項(xiàng)的引用被破壞或不存在時(shí),將引發(fā)此錯(cuò)誤。也就是說,變量/項(xiàng)不存在。
例如,
我們有一個(gè)變量cat初始化為“ cat”。接下來,我們引用cat變量和dog變量。cat變量存在,而dog變量不存在。
cat將返回“ cat”,而dog會(huì)引發(fā)ReferenceError,因?yàn)樵诃h(huán)境記錄中找不到名為dog的變量。
每當(dāng)我們創(chuàng)建或定義變量時(shí),變量名稱都會(huì)寫入環(huán)境記錄中。此環(huán)境記錄就像鍵值存儲(chǔ)表一樣,如下圖:
每當(dāng)我們引用變量時(shí),它都會(huì)存儲(chǔ)程序中定義的變量。當(dāng)在記錄中找到環(huán)境值并提取并返回值時(shí),將以該變量的名稱作為關(guān)鍵字搜索環(huán)境記錄。調(diào)用尚未定義的函數(shù)。
現(xiàn)在,當(dāng)我們創(chuàng)建或定義一個(gè)沒有賦值的變量時(shí)。變量將鍵作為變量名寫入環(huán)境記錄,但該值將保持未定義狀態(tài)。
稍后為變量分配值時(shí),將在env記錄中搜索該變量,當(dāng)發(fā)現(xiàn)該初始未定義值時(shí),該賦值將被覆蓋。
因此,當(dāng)在env記錄中找不到變量名時(shí),JS引擎會(huì)拋出RefernceError。
注意:未定義的變量不會(huì)拋出ReferenceError,因?yàn)樗嬖谟诃h(huán)境記錄中只是它的值尚未設(shè)置。
3. SyntaxError
這是我們遇到的最常見的錯(cuò)誤。當(dāng)我們鍵入JS引擎難以理解的代碼時(shí),會(huì)出現(xiàn)此錯(cuò)誤。解析期間,JS引擎捕獲了此錯(cuò)誤。
在JS引擎中,我們的代碼經(jīng)歷了不同的階段,然后才能在終端上看到運(yùn)行結(jié)果。
標(biāo)記化
解析
執(zhí)行
標(biāo)記化將源代碼分解為各個(gè)單元。在這個(gè)階段,將對(duì)數(shù)字,關(guān)鍵字,文字,運(yùn)算符進(jìn)行分類并分別進(jìn)行標(biāo)記。接下來,生成的token流將傳遞到解析階段,由解析器處理。這是從token生成AST的地方。AST是我們代碼結(jié)構(gòu)的抽象數(shù)據(jù)結(jié)構(gòu)。
在標(biāo)記化和解析這兩個(gè)階段,如果我們代碼的語法不符合JS的語法規(guī)則,則會(huì)使執(zhí)行階段失敗并引發(fā)SyntaxError。例如,
這里的“h”明顯是多余的,所以由于多了這個(gè)字符,會(huì)導(dǎo)致引擎拋出SyntaxError
很顯然,Node.js引擎發(fā)現(xiàn)了錯(cuò)誤,由于這個(gè)不和諧字符的出現(xiàn),導(dǎo)致cat變量的聲明失敗了。
4. TypeError
TypeError 是指對(duì)象用來表示值的類型非預(yù)期類型時(shí)發(fā)生的錯(cuò)誤。例如,我們期望它是布爾值,但結(jié)果發(fā)現(xiàn)它是string類型。
再例如:
因?yàn)閠oUpperCase函數(shù)需要字符串?dāng)?shù)據(jù)類型。toUpperCase函數(shù)是有意通用的;它不需要其this值是String對(duì)象。因此,可以將其轉(zhuǎn)移到其他種類的對(duì)象中用作方法。
如果我們?cè)贠bjects,Boolean,Symbol,null,undefined數(shù)據(jù)類型上調(diào)用toUpperCase函數(shù),則只有字符串會(huì)轉(zhuǎn)換為大寫或小寫形式,我們將得到TypeError,因?yàn)樗僮鞯臄?shù)據(jù)類型錯(cuò)誤。
5. URIError
這說明了使用一種全局URI處理功能與其定義不兼容。
JS中的URI(統(tǒng)一資源指示符)具有以下功能:decodeURI,decodeURIComponent等。
如果我們用錯(cuò)誤的參數(shù)調(diào)用其中任何一個(gè),我們將得到一個(gè)URIError。
encodeURI,獲取URI的未編碼版本。“%”不是正確的URI,因此引發(fā)了URIError。
編碼或解碼URI時(shí)出現(xiàn)問題時(shí),將引發(fā)URIError。
6. EvalError
如果非法調(diào)用 eval(),則拋出 EvalError 異常。
根據(jù)EcmaSpec 2018版:
此異常不再會(huì)被JavaScript拋出,但是EvalError對(duì)象仍然保持兼容性。
7. InternalError
該錯(cuò)誤在JS引擎內(nèi)部發(fā)生,特別是當(dāng)它有太多數(shù)據(jù)要處理并且堆棧增長(zhǎng)超過其關(guān)鍵限制時(shí)。
當(dāng)JS引擎被太多的遞歸,太多的切換情況等淹沒時(shí),就會(huì)發(fā)生這種情況。
太多的遞歸,一個(gè)簡(jiǎn)單的例子是這樣的:
結(jié)論
正如我們所說,沒有人能不犯錯(cuò)誤。就我們輸入的代碼而言,發(fā)生錯(cuò)誤是難以避免的。
不過為了避免更多的錯(cuò)誤出現(xiàn),我們需要知道拋出的錯(cuò)誤的類型是什么,我們?cè)撊绾谓鉀Q。
所以我們?cè)谶@篇文章中列出了它們,并提供了一些示例來簡(jiǎn)要的來介紹了它們是如何發(fā)生的。