墨菲定律指出,任何可能出錯的事情最終都會出錯。這在編程世界中應用得太好了。如果您創建一個應用程序,您很可能會產生錯誤和其他問題。JavaScript 中的錯誤就是這樣一個常見問題!
Kinsta 的託管服務獲得了數百個五星級評級。每天。
真的很喜歡 Kinsta 的實時聊天工程師提供的經驗水平和支持。24/7 支持是大型網站的遊戲規則改變者,尤其是電子商務。
利蘭·薩倫巴
找出為什麼
軟體產品的成功取決於其創建者在傷害用戶之前解決這些問題的能力。在所有編程語言中,JavaScript 因其平均錯誤處理設計而臭名昭著。
如果您正在構建一個 JavaScript 應用程序,那麼您很有可能會在某一時刻弄亂數據類型。如果不是這樣,那麼您最終可能會將 undefined 替換為 null 或將三等號運算符 (===) 替換為雙等號運算符 (==)。
犯錯是人之常情。這就是為什麼我們將向您展示您需要了解的有關處理 JavaScript 錯誤的所有信息。
本文將引導您了解 JavaScript 中的基本錯誤,並解釋您可能遇到的各種錯誤。然後,您將學習如何識別和修復這些錯誤。還有一些技巧可以在生產環境中有效地處理錯誤。
事不宜遲,讓我們開始吧!
什麼是 JavaScript 錯誤?
編程錯誤是指程序無法正常運行的情況。當程序不知道如何處理手頭的工作時,可能會發生這種情況,例如在沒有網路連接的情況下嘗試打開不存在的文件或訪問基於 Web 的 API 端點時。
這些情況促使程序向用戶拋出錯誤,說明它不知道如何繼續。該程序收集儘可能多的有關錯誤的信息,然後報告它無法繼續前進。
聰明的程序員試圖預測和覆蓋這些場景,這樣用戶就不必獨立地找出像「404」這樣的技術錯誤消息。相反,它們顯示了一條更容易理解的信息:「找不到該頁面。」
JavaScript 中的錯誤是在發生編程錯誤時顯示的對象。這些對象包含有關錯誤類型、導致錯誤的語句以及發生錯誤時的堆棧跟蹤的大量信息。JavaScript 還允許程序員創建自定義錯誤,以便在調試問題時提供額外信息。
錯誤的屬性
現在 JavaScript 錯誤的定義已經很清楚了,是時候深入研究細節了。
JavaScript 中的錯誤帶有某些標準和自定義屬性,有助於理解錯誤的原因和影響。默認情況下,JavaScript 中的錯誤包含三個屬性:
- message:攜帶錯誤信息的字元串值
- 名稱:發生的錯誤類型(我們將在下一節深入探討)
- stack:發生錯誤時執行的代碼的堆棧跟蹤。
此外,error 還可以攜帶 columnNumber、lineNumber、fileName 等屬性,以更好地描述錯誤。但是,這些屬性不是標準的,可能會出現在 JavaScript 應用程序生成的每個錯誤對象中,也可能不會出現。
了解堆棧跟蹤
堆棧跟蹤是發生異常或警告等事件時程序所在的方法調用列表。這是伴隨異常的示例堆棧跟蹤的樣子:
堆棧跟蹤示例。
如您所見,它首先列印錯誤名稱和消息,然後是被調用的方法列表。每個方法調用都說明其源代碼的位置以及調用它的行。您可以使用這些數據瀏覽您的代碼庫並確定是哪段代碼導致了錯誤。
此方法列表以堆疊方式排列。它顯示了您的異常首次引發的位置以及它如何通過堆疊的方法調用傳播。為異常實現捕獲不會讓它通過堆棧向上傳播並使您的程序崩潰。但是,您可能希望在某些情況下故意不捕獲致命錯誤以使程序崩潰。
錯誤與異常
大多數人通常將錯誤和異常視為同一件事。但是,必須注意它們之間的細微但根本的區別。
異常是已拋出的錯誤對象。
為了更好地理解這一點,讓我們舉一個簡單的例子。以下是如何在 JavaScript 中定義錯誤:
const wrongTypeError = TypeError(“發現錯誤類型,預期字元”)
這就是 wrongTypeError 對象變成異常的方式:
拋出錯誤類型錯誤
然而,大多數人傾向於使用在拋出錯誤對象時定義錯誤對象的簡寫形式:
throw TypeError(“發現錯誤類型,預期字元”)
這是標準做法。然而,這也是開發人員傾向於混淆異常和錯誤的原因之一。因此,即使您使用速記來快速完成工作,了解基礎知識也至關重要。
JavaScript 中的錯誤類型
JavaScript 中有一系列預定義的錯誤類型。只要程序員沒有明確處理應用程序中的錯誤,它們就會由 JavaScript 運行時自動選擇和定義。
本節將引導您了解 JavaScript 中一些最常見的錯誤類型,並了解它們發生的時間和原因。
範圍錯誤
當變數設置為超出其合法值範圍時,會引發 RangeError。它通常發生在將值作為參數傳遞給函數時,並且給定值不在函數參數的範圍內。使用文檔記錄不佳的第三方庫時,有時修復起來會很棘手,因為您需要知道參數的可能值範圍才能傳遞正確的值。
RangeError 發生的一些常見場景是:
- 試圖通過 Array 構造函數創建一個非法長度的數組。
- 將錯誤值傳遞給數字方法,例如 toExponential()、toPrecision()、toFixed() 等。
- 將非法值傳遞給像 normalize() 這樣的字元串函數。
參考錯誤
當您的代碼中的變數引用出現問題時,就會發生 ReferenceError。您可能忘記在使用變數之前為其定義值,或者您可能試圖在代碼中使用不可訪問的變數。在任何情況下,通過堆棧跟蹤提供了充足的信息來查找和修復有錯誤的變數引用。
ReferenceErrors 發生的一些常見原因是:
- 在變數名中打錯字。
- 試圖訪問其範圍之外的塊範圍變數。
- 在載入之前從外部庫(如來自 jQuery 的 $)中引用全局變數。
語法錯誤
這些錯誤是最容易修復的錯誤之一,因為它們表明代碼語法有錯誤。由於 JavaScript 是一種解釋而不是編譯的腳本語言,因此當應用程序執行包含錯誤的腳本時會拋出這些腳本語言。在編譯語言的情況下,在編譯過程中會識別出此類錯誤。因此,在修復之前不會創建應用程序二進位文件。
可能發生 SyntaxErrors 的一些常見原因是:
- 缺少引號
- 缺少右括弧
- 花括弧或其他字元的不正確對齊
最好在 IDE 中使用 linting 工具在此類錯誤出現在瀏覽器之前為您識別這些錯誤。
類型錯誤
TypeError 是 JavaScript 應用程序中最常見的錯誤之一。當某些值不是特定的預期類型時,會創建此錯誤。發生時的一些常見情況是:
- 調用不是方法的對象。
- 試圖訪問空對象或未定義對象的屬性
- 將字元串視為數字,反之亦然
發生 TypeError 的可能性還有很多。稍後我們將查看一些著名的實例並學習如何修復它們。
內部錯誤
當 JavaScript 運行時引擎中發生異常時使用 InternalError 類型。它可能表示也可能不表示您的代碼存在問題。
通常,InternalError 僅在兩種情況下發生:
- 當 JavaScript 運行時的補丁或更新帶有引發異常的錯誤時(這種情況很少發生)
- 當你的代碼包含對 JavaScript 引擎來說太大的實體時(例如太多的 switch case、太大的數組初始化器、太多的遞歸)
解決此錯誤的最合適方法是通過錯誤消息確定原因,並在可能的情況下重構您的應用程序邏輯,以消除 JavaScript 引擎上的工作負載突然激增。
URI錯誤
當非法使用 decodeURIComponent 等全局 URI 處理函數時,會發生 URIError。它通常表示傳遞給方法調用的參數不符合 URI 標準,因此沒有被方法正確解析。
診斷這些錯誤通常很容易,因為您只需要檢查參數是否存在畸形。
評估錯誤
當 eval() 函數調用發生錯誤時,會發生 EvalError。eval() 函數用於執行存儲在字元串中的 JavaScript 代碼。但是,由於安全問題,我們強烈建議不要使用 eval() 函數,並且當前的 ECMAScript 規範不再拋出 EvalError 類,所以這種錯誤類型的存在只是為了保持與舊版 JavaScript 代碼的向後兼容性。
如果您使用的是舊版本的 JavaScript,則可能會遇到此錯誤。無論如何,最好調查在 eval() 函數調用中執行的代碼是否有任何異常。
創建自定義錯誤類型
雖然 JavaScript 提供了足夠的錯誤類型列表來涵蓋大多數場景,但如果列表不滿足您的要求,您始終可以創建新的錯誤類型。這種靈活性的基礎在於 JavaScript 允許您使用 throw 命令逐字地拋出任何東西。
因此,從技術上講,這些聲明是完全合法的:
throw 8
throw “發生錯誤”
但是,拋出原始數據類型不會提供有關錯誤的詳細信息,例如其類型、名稱或隨附的堆棧跟蹤。為了解決這個問題並標準化錯誤處理過程,我們提供了 Error 類。也不鼓勵在拋出異常時使用原始數據類型。
您可以擴展 Error 類來創建您的自定義錯誤類。以下是如何執行此操作的基本示例:
類 ValidationError 擴展錯誤 {
構造函數(消息) {
超級(消息);
this.name = “驗證錯誤”;
}
}
您可以通過以下方式使用它:
throw ValidationError(“找不到屬性:名稱”)
然後您可以使用 instanceof 關鍵字來識別它:
try {
validateForm() // 拋出 ValidationError 的代碼
} catch (e) {
if (e instanceof ValidationError)
// 做其他
事情
// 做其他事情
}
JavaScript 中最常見的 10 個錯誤
現在您已經了解了常見的錯誤類型以及如何創建自定義錯誤類型,是時候看看您在編寫 JavaScript 代碼時會遇到的一些最常見的錯誤了。
1. 未捕獲的 RangeError
在幾種不同的情況下,Google Chrome 中會出現此錯誤。首先,如果您調用遞歸函數並且它不會終止,則可能會發生這種情況。您可以在 Chrome 開發者控制台中自行查看:
帶有遞歸函數調用的 RangeError 示例。
因此,要解決此類錯誤,請確保正確定義遞歸函數的邊界情況。發生此錯誤的另一個原因是您傳遞的值超出了函數的參數範圍。這是一個例子:
帶有 toExponential() 調用的 RangeError 示例。
錯誤消息通常會指出您的代碼有什麼問題。一旦你做出改變,它就會得到解決。
toExponential() 函數調用的輸出。
2. Uncaught TypeError: Cannot set property
當您在未定義的引用上設置屬性時會發生此錯誤。您可以使用此代碼重現該問題:
var list
list.count = 0
這是您將收到的輸出:
類型錯誤示例。
要修復此錯誤,請在訪問其屬性之前使用值初始化引用。以下是修復後的外觀:
如何修復類型錯誤。
3. Uncaught TypeError: Cannot read property
這是 JavaScript 中最常出現的錯誤之一。當您嘗試讀取屬性或調用未定義對象的函數時,會發生此錯誤。您可以通過在 Chrome 開發人員控制台中運行以下代碼來非常輕鬆地重現它:
var func
func.call ()
這是輸出:
帶有未定義函數的 TypeError 示例。
未定義的對象是導致此錯誤的眾多可能原因之一。此問題的另一個突出原因可能是在呈現 UI 時未正確初始化狀態。這是來自 React 應用程序的真實示例:
從「反應」中導入反應,{ useState,useEffect };
常量 CardsList = () => {
常量 [狀態,setState] = useState();
useEffect(() => {
setTimeout(() => setState({ items: [“Card 1”, “Card 2”] }), 2000);
}, []);
返回 (
<>
{state.items.map((item) => (
<li key={item}>{item}</li>
))}
</>
);
};
導出默認 CardsList;
該應用程序從一個空狀態容器開始,並在 2 秒延遲後提供一些項目。延遲用於模擬網路調用。即使您的網路速度非常快,您仍然會面臨輕微的延遲,因為該組件將至少呈現一次。如果您嘗試運行此應用程序,您將收到以下錯誤:
瀏覽器中的 TypeError 堆棧跟蹤。
這是因為,在渲染時,狀態容器是未定義的;因此,上面不存在任何屬性項。修復這個錯誤很容易。您只需要為狀態容器提供初始默認值。
// …
const [state, setState] = useState({items: []});
// …
現在,在設置延遲之後,您的應用程序將顯示類似的輸出:
代碼輸出。
您的代碼中的確切修復可能會有所不同,但這裡的本質是在使用它們之前始終正確初始化您的變數。
4. TypeError: ‘undefined’ is not an object
當您嘗試訪問未定義對象的屬性或調用未定義對象的方法時,會在 Safari 中發生此錯誤。您可以從上面運行相同的代碼來自己重現錯誤。
帶有未定義函數的 TypeError 示例。
這個錯誤的解決方法也是一樣的——確保你已經正確地初始化了你的變數,並且在訪問一個屬性或方法時它們不是未定義的。
5. TypeError: null is not an object
這又與前面的錯誤相似。它發生在 Safari 上,這兩個錯誤之間的唯一區別是,當正在訪問其屬性或方法的對象為 null 而不是 undefined 時,會拋出這個錯誤。您可以通過運行以下代碼來重現這一點:
變數函數 = 空
func.call()
這是您將收到的輸出:
帶有 null 函數的 TypeError 示例。
因為 null 是顯式設置為變數的值,而不是由 JavaScript 自動分配的值。僅當您嘗試訪問自己設置為 null 的變數時,才會發生此錯誤。因此,您需要重新訪問您的代碼並檢查您編寫的邏輯是否正確。
6. TypeError: 無法讀取屬性’length’
當您嘗試讀取 null 或未定義對象的長度時,Chrome 會出現此錯誤。這個問題的原因和前面的問題類似,但是在處理列表的時候出現的頻率比較高;因此值得特別提及。以下是重現問題的方法:
帶有未定義對象的 TypeError 示例。
但是,在較新版本的 Chrome 中,此錯誤報告為 Uncaught TypeError: Cannot read properties of undefined。這是它現在的樣子:
在較新的 Chrome 版本上帶有未定義對象的 TypeError 示例。
再次,修復是確保您嘗試訪問其長度的對象存在並且未設置為空。
7. TypeError: ‘undefined’ is not a function
當您嘗試調用腳本中不存在的方法或該方法存在但無法在調用上下文中引用時,會發生此錯誤。這個錯誤通常發生在谷歌瀏覽器中,您可以通過檢查拋出錯誤的代碼行來解決它。如果您發現拼寫錯誤,請修復它並檢查它是否能解決您的問題。
如果您在代碼中使用了自引用關鍵字 this,如果 this 沒有適當地綁定到您的上下文,則可能會出現此錯誤。考慮以下代碼:
函數 showAlert() {
alert(“這裡的信息”)
}
document.addEventListener(“click”, () => {
this.showAlert();
})
如果你執行上面的代碼,它會拋出我們討論過的錯誤。發生這種情況是因為作為事件偵聽器傳遞的匿名函數正在文檔的上下文中執行。
相反,函數 showAlert 是在窗口的上下文中定義的。
要解決這個問題,您必須通過使用 bind() 方法綁定函數來傳遞對函數的正確引用:
document.addEventListener(“點擊”, this.showAlert.bind(this))
8. ReferenceError: event is not defined
當您嘗試訪問未在調用範圍內定義的引用時,會發生此錯誤。這通常在處理事件時發生,因為它們通常會在回調函數中為您提供一個名為 event 的引用。如果您忘記在函數的參數中定義事件參數或拼寫錯誤,則可能會發生此錯誤。
訂閱時事通訊
想知道我們是如何將流量提高到 1000% 以上的嗎?
加入 20,000 多人的行列,他們會通過 WordPress 內幕技巧獲得我們的每周時事通訊!
現在訂閱
此錯誤可能不會在 Internet Explorer 或 Google Chrome 中發生(因為 IE 提供了一個全局事件變數並且 Chrome 會自動將事件變數附加到處理程序),但它可能會在 Firefox 中發生。所以建議留意這樣的小錯誤。
9. TypeError:賦值給常量變數
這是由於粗心造成的錯誤。如果您嘗試將新值分配給常量變數,您將遇到這樣的結果:
帶有常量對象分配的 TypeError 示例。
雖然現在看起來很容易修復,但想像一下數百個這樣的變數聲明,其中一個被錯誤地定義為 const 而不是 let!與 PHP 等其他腳本語言不同,在 JavaScript 中聲明常量和變數的風格差別很小。因此,當您遇到此錯誤時,建議首先檢查您的聲明。如果您忘記上述引用是一個常量並將其用作變數,您也可能會遇到此錯誤。這表明您的應用程序邏輯存在粗心或缺陷。嘗試解決此問題時,請務必檢查此項。
10.(未知):腳本錯誤
當第三方腳本向您的瀏覽器發送錯誤時,就會發生腳本錯誤。此錯誤後跟(未知),因為第三方腳本與您的應用屬於不同的域。瀏覽器隱藏了其他細節,以防止第三方腳本泄露敏感信息。
在不了解完整詳細信息的情況下,您無法解決此錯誤。您可以執行以下操作來獲取有關該錯誤的更多信息:
- 在腳本標籤中添加 crossorigin 屬性。
- 在託管腳本的伺服器上設置正確的 Access-Control-Allow-Origin 標頭。
- [可選] 如果您無權訪問託管腳本的伺服器,您可以考慮使用代理將您的請求中繼到伺服器並返回到帶有正確標頭的客戶端。
一旦您可以訪問錯誤的詳細信息,您就可以著手解決問題,這可能與第三方庫或網路有關。
如何識別和防止 JavaScript 中的錯誤
雖然上面討論的錯誤是 JavaScript 中最常見和最常見的錯誤,但您會遇到,僅僅依靠幾個示例是遠遠不夠的。在開發 JavaScript 應用程序時,了解如何檢測和防止任何類型的錯誤至關重要。以下是如何處理 JavaScript 中的錯誤。
手動拋出和捕獲錯誤
處理手動或運行時拋出的錯誤的最基本方法是捕獲它們。與大多數其他語言一樣,JavaScript 提供了一組關鍵字來處理錯誤。在著手處理 JavaScript 應用程序中的錯誤之前,必須深入了解它們中的每一個。
扔
該集合的第一個也是最基本的關鍵字是 throw。很明顯,throw 關鍵字用於在 JavaScript 運行時中手動拋出錯誤以創建異常。我們已經在本文前面討論過這個問題,這裡是這個關鍵字意義的要點:
- 你可以拋出任何東西,包括數字、字元串和錯誤對象。
- 但是,不建議拋出諸如字元串和數字之類的原始數據類型,因為它們不攜帶有關錯誤的調試信息。
- 示例: throw TypeError(“請提供一個字元串”)
嘗試
try 關鍵字用於指示代碼塊可能會引發異常。它的語法是:
try {
// 這裡有錯誤的代碼
}
需要注意的是,catch 塊必須始終跟在 try 塊之後才能有效地處理錯誤。
抓住
catch 關鍵字用於創建一個 catch 塊。此代碼塊負責處理尾隨 try 塊捕獲的錯誤。這是它的語法:
catch (exception) {
// 此處處理異常的代碼
}
這就是一起實現 try 和 catch 塊的方式:
try {
// 業務邏輯代碼
} catch (exception) {
// 錯誤處理代碼
}
與 C++ 或 Java 不同,您不能將多個 catch 塊附加到 JavaScript 中的 try 塊。這意味著您不能這樣做:
try {
// 業務邏輯代碼
} catch (exception) {
if (exception instanceof TypeError) {
// 做某事
}
} catch (exception) {
if (exception instanceof RangeError) {
// 做某事
}
}
相反,您可以在單個 catch 塊中使用 if…else 語句或 switch case 語句來處理所有可能的錯誤情況。它看起來像這樣:
try {
// 業務邏輯代碼
} catch (exception) {
if (exception instanceof TypeError) {
// do something
} else if (exception instanceof RangeError) {
// do something else
}
}
finally
finally 關鍵字用於定義處理錯誤後運行的代碼塊。該塊在 try 和 catch 塊之後執行。
此外,無論其他兩個塊的結果如何,都會執行 finally 塊。這意味著即使catch塊不能完全處理錯誤或者catch塊中拋出錯誤,解釋器也會在程序崩潰之前執行finally塊中的代碼。
要被認為是有效的,JavaScript 中的 try 塊需要後跟 catch 或 finally 塊。如果沒有這些,解釋器將引發 SyntaxError。因此,在處理錯誤時,請確保至少遵循您的 try 塊。
使用 onerror() 方法全局處理錯誤
onerror() 方法可用於所有 HTML 元素,以處理它們可能發生的任何錯誤。例如,如果 img 標籤找不到指定 URL 的圖像,它會觸發其 onerror 方法以允許用戶處理錯誤。
通常,您會在 onerror 調用中提供另一個圖像 URL,以便 img 標記回退到。這是您可以通過 JavaScript 執行此操作的方法:
const image = document.querySelector(“img”)
image.onerror = (event) => {
console.log(“錯誤發生:” + event)
}
但是,您可以使用此功能為您的應用創建全局錯誤處理機制。以下是您的操作方法:
window.onerror = (event) => {
console.log(“錯誤發生:” + event)
}
使用此事件處理程序,您可以擺脫代碼中的多個 try…catch 塊,並集中您的應用程序的錯誤處理,類似於事件處理。您可以將多個錯誤處理程序附加到窗口,以維護 SOLID 設計原則中的單一責任原則。解釋器將循環遍歷所有處理程序,直到到達適當的處理程序。
通過回調傳遞錯誤
雖然簡單和線性函數允許錯誤處理保持簡單,但回調會使事情複雜化。
考慮以下代碼:
需要一個可以為您提供競爭優勢的託管解決方案?Kinsta 為您提供令人難以置信的速度、最先進的安全性和自動縮放功能。查看我們的計劃
const calculateCube = (number, callback) => {
setTimeout(() => {
const cube = number * number * number
callback(cube)
}, 1000)
}
常量回調 = 結果 => 控制台日誌(結果)
計算立方(4,回調)
上面的函數演示了一個非同步條件,其中一個函數需要一些時間來處理操作並稍後在回調的幫助下返回結果。
如果您嘗試在函數調用中輸入字元串而不是 4,您將得到 NaN 作為結果。
這需要妥善處理。就是這樣:
const calculateCube = (數字, 回調) => {
setTimeout(() => {
if (typeof number !== “number”)
throw new Error(“Numeric argument is expected”)
const cube = number * number * number
callback(cube)
}, 1000)
}
常量回調 = 結果 => 控制台日誌(結果)
嘗試 {
calculateCube(4, callback)
} catch (e) { console.log(e) }
這應該可以理想地解決問題。但是,如果您嘗試將字元串傳遞給函數調用,您將收到以下信息:
錯誤參數的錯誤示例。
即使您在調用函數時實現了 try-catch 塊,它仍然表示錯誤未捕獲。由於超時延遲,在執行 catch 塊後拋出錯誤。
這可能在網路調用中很快發生,在這種情況下會出現意外延遲。您需要在開發應用程序時涵蓋此類情況。
以下是在回調中正確處理錯誤的方法:
const calculateCube = (數字, 回調) => {
setTimeout(() => {
if (typeof number !== “number”) {
callback(new TypeError(“Numeric argument is expected”))
return
}
const cube = number * number * number
callback(null, cube)
}, 2000)
}
const callback = (error, result) => {
if (error !== null) {
console.log(error)
return
}
console.log(result)
}
嘗試 {
calculateCube(‘hey’, callback)
} catch (e) {
console.log(e)
}
現在,控制台的輸出將是:
帶有非法參數的 TypeError 示例。
這表明錯誤已得到適當處理。
處理 Promise 中的錯誤
大多數人傾向於使用 Promise 來處理非同步活動。Promise 還有另一個優點——被拒絕的 Promise 不會終止你的腳本。但是,您仍然需要實現一個 catch 塊來處理 Promise 中的錯誤。為了更好地理解這一點,讓我們使用 Promises 重寫 calculateCube() 函數:
常量延遲 = ms => 新承諾(res => setTimeout(res,ms));
const calculateCube = async (number) => {
if (typeof number !== “number”)
throw Error(“Numeric argument is expected”)
await delay(5000)
const cube = number * number * number
return cube
}
嘗試 {
calculateCube(4).then(r => console.log(r))
} 捕捉 (e) { console.log(e) }
前面代碼中的超時已經被隔離到延遲函數中以便理解。如果您嘗試輸入一個字元串而不是 4,您獲得的輸出將類似於以下內容:
在 Promise 中帶有非法參數的 TypeError 示例。
同樣,這是由於 Promise 在其他所有內容完成執行後拋出錯誤。這個問題的解決方案很簡單。只需像這樣向 Promise 鏈添加一個 catch() 調用:
計算Cube(“嘿”)
.then(r => console.log(r))
.catch(e => console.log(e))
現在輸出將是:
處理帶有非法參數的 TypeError 示例。
您可以觀察到使用 Promise 處理錯誤是多麼容易。此外,您可以鏈接 finally() 塊和 promise 調用以添加將在錯誤處理完成後運行的代碼。
或者,您也可以使用傳統的 try-catch-finally 技術來處理 Promise 中的錯誤。在這種情況下,您的 promise 調用如下所示:
try {
let result = await calculateCube(“hey”)
console.log(result)
} catch (e) {
console.log(e)
} finally {
console.log(‘Finally executed”)
}
但是,這僅適用於非同步函數。因此,處理 Promise 中的錯誤的最優選方式是鏈式 catch,最後連接到 Promise 調用。
throw/catch vs onerror() vs Callbacks vs Promises:哪個是最好的?
有四種方法可供您使用,您必須知道如何在任何給定的用例中選擇最合適的方法。以下是您可以自己決定的方法:
扔/接
您將在大多數情況下使用此方法。確保在你的 catch 塊中為所有可能的錯誤實現條件,如果你需要在 try 塊之後運行一些內存清理常式,請記住包含一個 finally 塊。
但是,太多的 try/catch 塊會使您的代碼難以維護。如果您發現自己處於這種情況,您可能希望通過全局處理程序或 promise 方法來處理錯誤。
在非同步 try/catch 塊和 promise 的 catch() 之間做出決定時,建議使用非同步 try/catch 塊,因為它們將使您的代碼線性且易於調試。
錯誤()
當您知道您的應用程序必須處理許多錯誤並且它們可以很好地分散在整個代碼庫中時,最好使用 onerror() 方法。onerror 方法使您能夠處理錯誤,就好像它們只是您的應用程序處理的另一個事件一樣。您可以定義多個錯誤處理程序並在初始呈現時將它們附加到應用程序的窗口。
但是,您還必須記住,在錯誤範圍較小的較小項目中設置 onerror() 方法可能會帶來不必要的挑戰。如果您確定您的應用程序不會拋出太多錯誤,那麼傳統的 throw/catch 方法將最適合您。
回調和承諾
回調和承諾中的錯誤處理因代碼設計和結構而異。但是,如果您在編寫代碼之前在這兩者之間進行選擇,最好使用 Promise。
這是因為 Promise 有一個內置結構,用於鏈接 catch() 和 finally() 塊來輕鬆處理錯誤。這種方法比定義附加參數/重用現有參數來處理錯誤更容易和更清晰。
使用 Git 存儲庫跟蹤更改
由於代碼庫中的手動錯誤,經常會出現許多錯誤。在開發或調試代碼時,您最終可能會進行不必要的更改,這可能會導致代碼庫中出現新的錯誤。自動化測試是在每次更改後檢查代碼的好方法。但是,它只能告訴您是否有問題。如果你不經常備份你的代碼,你最終會浪費時間試圖修復一個以前運行良好的函數或腳本。
這就是 git 發揮作用的地方。通過適當的提交策略,您可以使用 git 歷史記錄作為備份系統來查看代碼在開發過程中的演變過程。您可以輕鬆瀏覽舊提交並找出該函數的版本之前運行良好,但在不相關的更改後拋出錯誤。
然後,您可以恢復舊代碼或比較兩個版本以確定哪裡出了問題。現代 Web 開發工具(如 GitHub Desktop 或 GitKraken)可幫助您並排可視化這些更改並快速找出錯誤。
一個可以幫助您減少錯誤的習慣是在您對代碼進行重大更改時運行代碼審查。如果您在一個團隊中工作,您可以創建一個拉取請求並讓團隊成員徹底審查它。這將幫助您使用第二雙眼睛來發現您可能遺漏的任何錯誤。
處理 JavaScript 錯誤的最佳實踐
上述方法足以幫助您為下一個 JavaScript 應用程序設計一個健壯的錯誤處理方法。但是,最好在實施時記住一些事情,以充分利用您的防錯功能。這裡有一些提示可以幫助您。
1. 處理操作異常時使用自定義錯誤
我們在本指南的前面介紹了自定義錯誤,讓您了解如何根據應用程序的獨特情況自定義錯誤處理。建議儘可能使用自定義錯誤而不是通用 Error 類,因為它為調用環境提供了有關錯誤的更多上下文信息。
最重要的是,自定義錯誤允許您調整錯誤在調用環境中的顯示方式。這意味著您可以根據需要選擇隱藏特定詳細信息或顯示有關錯誤的其他信息。
您可以根據需要格式化錯誤內容。這使您可以更好地控制錯誤的解釋和處理方式。
2.不要吞下任何例外
即使是最資深的開發人員也經常犯一個新手錯誤——在他們的代碼中使用異常級別。
您可能會遇到有一段代碼可以選擇運行的情況。如果它有效,那就太好了;如果沒有,你不需要做任何事情。
在這些情況下,通常很想將這段代碼放在 try 塊中,並在其上附加一個空的 catch 塊。但是,通過這樣做,您將使那段代碼保持開放狀態,從而導致任何類型的錯誤並逃脫懲罰。如果你有一個龐大的代碼庫和許多這樣糟糕的錯誤管理結構的實例,這可能會變得很危險。
處理異常的最佳方法是確定所有異常都將被處理的級別並將它們提高到那裡。此級別可以是控制器(在 MVC 架構應用程序中)或中間件(在傳統的面向伺服器的應用程序中)。
通過這種方式,您將了解在哪裡可以找到應用程序中發生的所有錯誤並選擇如何解決它們,即使這意味著不對它們做任何事情。
3. 對日誌和錯誤警報使用集中策略
記錄錯誤通常是處理錯誤的一個組成部分。那些未能制定集中策略來記錄錯誤的人可能會錯過有關其應用程序使用情況的寶貴信息。
應用程序的事件日誌可以幫助您找出有關錯誤的關鍵數據並幫助快速調試它們。如果您在應用程序中設置了適當的警報機制,您可以在錯誤到達大部分用戶群之前知道應用程序何時發生錯誤。
建議使用預先構建的記錄器或創建一個以滿足您的需求。您可以配置此記錄器以根據其級別(警告、調試、信息等)處理錯誤,一些記錄器甚至可以立即將日誌發送到遠程記錄伺服器。通過這種方式,您可以觀察應用程序的邏輯在活動用戶中的執行情況。
4. 適當地通知用戶錯誤
在定義錯誤處理策略時要牢記的另一個要點是牢記用戶。
所有干擾您的應用程序正常運行的錯誤都必須向用戶顯示可見的警報,以通知他們出現問題,以便用戶可以嘗試製定解決方案。如果您知道錯誤的快速修復方法,例如重試操作或註銷並重新登錄,請務必在警報中提及它以幫助實時修復用戶體驗。
如果錯誤不會對日常用戶體驗造成任何干擾,您可以考慮抑制警報並將錯誤記錄到遠程伺服器以供以後解決。
5. 實現一個中間件(Node.js)
Node.js 環境支持中間件向伺服器應用程序添加功能。您可以使用此功能為您的伺服器創建錯誤處理中間件。
使用中間件的最大好處是所有錯誤都集中在一個地方處理。您可以輕鬆選擇啟用/禁用此設置以進行測試。
以下是創建基本中間件的方法:
const logError = err => {
console.log(“ERROR: ” + String(err))
}
const errorLoggerMiddleware = (err, req, res, next) => {
logError(err)
next(err)
}
const returnErrorMiddleware = (err, req, res, next) => {
res.status(err.statusCode || 500)
.send(err.message)
}
module.exports = { logError
,
errorLoggerMiddleware,
returnErrorMiddleware
}
然後,您可以在您的應用程序中使用此中間件,如下所示:
const { errorLoggerMiddleware, returnErrorMiddleware } = require(‘./errorMiddleware’)
app.use(errorLoggerMiddleware)
app.use(returnErrorMiddleware)
您現在可以在中間件中定義自定義邏輯以適當地處理錯誤。您不再需要擔心在整個代碼庫中實現單個錯誤處理結構。
6. 重新啟動您的應用程序以處理程序員錯誤(Node.js)
當 Node.js 應用程序遇到程序員錯誤時,它們可能不一定會拋出異常並嘗試關閉應用程序。此類錯誤可能包括由程序員錯誤引起的問題,例如高 CPU 消耗、內存膨脹或內存泄漏。處理這些問題的最佳方法是通過 Node.js 集群模式或 PM2 等獨特工具使應用程序崩潰,從而優雅地重新啟動應用程序。這可以確保應用程序不會因用戶操作而崩潰,從而呈現糟糕的用戶體驗。
7. 捕獲所有未捕獲的異常(Node.js)
您永遠無法確定您已經涵蓋了您的應用程序中可能出現的所有錯誤。因此,實施備用策略以從您的應用程序中捕獲所有未捕獲的異常非常重要。
您可以這樣做:
process.on(‘uncaughtException’, error => {
console.log(“ERROR: ” + String(error))
// 其他處理機制
})
您還可以確定發生的錯誤是標準異常還是自定義操作錯誤。根據結果,您可以退出進程並重新啟動它以避免意外行為。
8. 捕獲所有未處理的 Promise Rejections (Node.js)
與您永遠無法涵蓋所有可能的異常類似,您很有可能會錯過處理所有可能的承諾拒絕。但是,與異常不同,promise 拒絕不會引發錯誤。
因此,一個被拒絕的重要承諾可能會作為警告而溜走,並使您的應用程序面臨遇到意外行為的可能性。因此,實現一個回退機制來處理 Promise 拒絕是至關重要的。
您可以這樣做:
const promiseRejectionCallback = error => {
console.log(“PROMISE REJECTED:” + String(error))
}
process.on(‘unhandledRejection’, callback)
如果您創建一個應用程序,您也有可能在其中創建錯誤和其他問題。😅 在本指南的幫助下了解如何處理它們⬇️點擊推文摘
要
與任何其他編程語言一樣,JavaScript 中的錯誤非常頻繁且自然。在某些情況下,您甚至可能需要故意拋出錯誤以向用戶指示正確的響應。因此,了解它們的解剖結構和類型非常重要。
此外,您需要配備正確的工具和技術來識別和防止錯誤導致您的應用程序崩潰。
在大多數情況下,對於所有類型的 JavaScript 應用程序來說,通過仔細執行來處理錯誤的可靠策略就足夠了。
是否還有其他您無法解決的 JavaScript 錯誤?有什麼建設性地處理 JS 錯誤的技術嗎?在下面的評論中讓我們知道!
通過以下方式節省時間、成本並最大限度地提高站點性能:
- 來自 WordPress 託管專家的即時幫助,24/7。
- Cloudflare 企業集成。
- 全球受眾覆蓋全球 29 個數據中心。
- 使用我們內置的應用程序性能監控進行優化。
所有這些以及更多,在一個沒有長期合同、協助遷移和 30 天退款保證的計劃中。查看我們的計劃或與銷售人員交談以找到適合您的計劃。