JavaScript的原生錯誤類型
從瀏覽器控制臺到運(yùn)行 Node.js 的終端,我們到處都會看到錯誤。
本文的重點是概述我們在 JS 開發(fā)過程中可能遇到的錯誤類型。
提示:良好的錯誤提示會導(dǎo)致快速而無痛的發(fā)展經(jīng)歷與緩慢而痛苦的發(fā)展經(jīng)歷之間的區(qū)別。在編寫可重用的代碼時,請確保自己在編寫清晰易懂的錯誤處理代碼。
1. RangeError
當(dāng)數(shù)字超出允許的值范圍時,將會拋出此錯誤。
例如
- const l = console.logconst arr = [90,88]
- arr.length=90**99
我們有一個數(shù)組,帶有兩個元素的 arr。接下來,嘗試將數(shù)組擴(kuò)展為包含 90**99 == 2.9512665430652753e+193 個元素。
這個數(shù)字超出了數(shù)組大小可以增長的范圍。運(yùn)行它會拋出 RangeError:
- $ node errors
- errors.js:4
- arr.length=90**99
- ^RangeError: Invalid array length
因為我們要增加 arr 數(shù)組的大小超出了 JS 指定的范圍。
2. ReferenceError
當(dāng)對變量或項目的引用被破壞時,將會引發(fā)此錯誤。那是變量或項目不存在。
例如
- const l=console.logconst cat = "cat"
- cat
- dog
有一個變量 cat 被初始化為 “cat”。接下來引用了 cat 變量和 dog 變量。cat變量存在,而 dog 變量不存在。
cat 將返回 “cat”,而 dog 將引發(fā)引用錯誤,因為在環(huán)境記錄中找不到名稱 dog。
- $ node errors
- errors.js:3
- dog
- ^ReferenceError: dog is not defined
每當(dāng)我們創(chuàng)建或定義變量時,變量名稱都會寫入環(huán)境記錄中。環(huán)境記錄就像鍵值存儲一樣,
- +-------------+
- | Key | Value |
- ---------------
- | cat | "cat" |
- +-------------+
每當(dāng)我們引用變量時,它都會存儲程序中定義的變量。當(dāng)在記錄中找到環(huán)境值并提取并返回值時,將以該變量的名稱作為關(guān)鍵字在環(huán)境記錄進(jìn)行搜索。調(diào)用尚未定義的函數(shù)。
現(xiàn)在,當(dāng)我們創(chuàng)建或定義一個沒有賦值的變量時。變量將其鍵作為變量名寫入環(huán)境記錄,但其值將會保持未定義的狀態(tài)。
- var catenv record
- +-----------------+
- | Key | Value |
- -------------------
- | cat | undefined |
- +-----------------+
稍后為變量分配值時,將在環(huán)境記錄中搜索該變量,當(dāng)發(fā)現(xiàn)它未定義值時,該賦值將被覆蓋。
- var cat
- cat = "cat"env record
- +-------------+
- | Key | Value |
- ---------------
- | cat | "cat" |
- +-------------+
所以當(dāng)在環(huán)境記錄中找不到變量名時,JS 引擎將引發(fā) RefernceError。
- +-------------+
- | Key | Value |
- ---------------
- | cat | "cat" |
- +-------------+cat // "cat", yes, :) it's there
- dog // :( what's this? can't find it
注意:未定義的變量不會拋出 ReferenceError,因為它在于環(huán)境記錄中的值尚未設(shè)置。
3. SyntaxError
這是最常見的錯誤。當(dāng)我們輸入 JS 引擎不能理解的代碼時,就會發(fā)生這個錯誤。
JS 引擎在解析期間捕獲了這個錯誤。在 JS 引擎中,我們的代碼經(jīng)歷了不同的階段,然后才能在終端上看到結(jié)果。
- 標(biāo)記化
- 解析
- 解釋
標(biāo)記化將代碼的源分解為各個單元。在此階段,將對數(shù)字、關(guān)鍵字、文字、運(yùn)算符進(jìn)行分類并分別標(biāo)記。
接下來,生成的標(biāo)記流將會傳遞到解析階段,由解析器處理。這是從標(biāo)記流生成 AST 的地方。AST 是代碼結(jié)構(gòu)的抽象表示。
在標(biāo)記化和解析這兩個階段,如果我們代碼的語法不符合 JS 的語法規(guī)則,則會使該階段失敗并引發(fā) SyntaxError。例如:
- const l = console.loglet cat h = "cat"
代碼中的 “h” 代表什么?這個 “h” 破壞了代碼。
- $ node errors
- errors.js:3
- let cat h = "cat"
- ^SyntaxError: Unexpected identifier
看,Node.js 指出了問題的所在。它說 “h” 是意外的,它破壞了cat 變量的聲明。
因此,可以說語法錯誤在解析或編譯期間發(fā)生。
4. TypeError
當(dāng)其他 NativeError 對象中沒有適當(dāng)?shù)氖≡虻闹甘緯r,TypeError 用于指示操作失敗。
對錯誤的數(shù)據(jù)類型執(zhí)行操作時會發(fā)生 TypeError,例如:
如果我們嘗試將數(shù)字轉(zhuǎn)換為大寫,如下所示:
- const num = 123
- num.toUpperCase()
這將引發(fā)TypeError
- $ node errors
- errors.js:4
- num.toUpperCase()
- ^TypeError: num.toUpperCase is not a function
因為 toUpperCase 函數(shù)需要字符串?dāng)?shù)據(jù)類型。toUpperCase 函數(shù)是有意通用的;它不需要其 this 值是 String對象。因此,可以將其轉(zhuǎn)移到其他種類的對象中用作方法。
只有字符串才會轉(zhuǎn)換為大寫或小寫形式,如果我們在 Objects、Boolean、Symbol、null、undefined 數(shù)據(jù)類型上調(diào)用 toUpperCase 函數(shù),則將會得到 TypeError,因為它操作的數(shù)據(jù)類型錯誤。
5. URIError
這表明使用了一種與其定義不兼容的全局 URI 處理函數(shù)。
JS 中的 URI(統(tǒng)一資源指示符)具有以下功能:decodeURI、decodeURIComponent 等。
如果我們用錯誤的參數(shù)去調(diào)用其中任何一個,將得會到一個 URIError。
- decodeURI("%")
- ^URIError: URI malformed
encodeURI 用于獲取 URI 的未編碼版本。“%” 不是正確的 URI,因此引發(fā)了URIError。
當(dāng) URI 編碼或解碼出現(xiàn)問題時,會引發(fā) URIError。
6. EvalError
當(dāng)使用全局 eval() 函數(shù)時,這用于識別錯誤。
根據(jù) EcmaSpec 2018 版:
此規(guī)范當(dāng)前未使用此異常。保留它目的是為了與本規(guī)范的先前版本兼容。
7. InternalError
該錯誤在 JS 引擎內(nèi)部發(fā)生,特別是當(dāng)它有太多數(shù)據(jù)要處理并且棧增長超過其關(guān)鍵限制的時侯。
當(dāng) JS 引擎被過多的遞歸和切換情況等淹沒時,就會發(fā)生這種問題
- switch(num) {
- case 1:
- ...
- break
- case 2:
- ...
- break
- case 3:
- ...
- break
- case 4:
- ...
- break
- case 5:
- ...
- break
- case 6:
- ...
- break
- case 7:
- ...
- break
- ... up to 1000 cases
- }
以下是一個簡單的過多遞歸的例子:
- function foo() {
- foo()
- }
- foo()
總結(jié)
正如我們所說,誰都會犯錯誤。就我們敲代碼這件事而言,這是一個穩(wěn)定的事件。為了克服它,我們需要知道可以拋出的原生錯誤的類型。本文中列出了它們,并提供了一些示例來說明它們是如何引發(fā)的。
所以無論什么時候在終端或瀏覽器中引發(fā)錯誤,你都可以輕松發(fā)現(xiàn)錯誤產(chǎn)生的位置和方式,并能夠編寫更好、更不易出錯的代碼。