新聞中心
墨菲定律指出,任何可能出錯的事情最終都會出錯。這在編程世界中應用得太好了。如果您創(chuàng)建一個應用程序,您很可能會產(chǎn)生錯誤和其他問題。JavaScript中的錯誤就是這樣一個常見問題!

讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:域名申請、網(wǎng)頁空間、營銷軟件、網(wǎng)站建設、市北網(wǎng)站維護、網(wǎng)站推廣。
軟件產(chǎn)品的成功取決于其創(chuàng)建者在傷害用戶之前解決這些問題的能力。在所有編程語言中, JavaScript因其平均錯誤處理設計而臭名昭著。
如果您正在構建一個JavaScript應用程序,那么您很有可能會在某一時刻弄亂數(shù)據(jù)類型。如果不是這樣,那么您最終可能會將undefined替換為null或將三等號運算符 ( ===) 替換為雙等號運算符 ( ==)。
犯錯是人之常情。這就是為什么我們將向您展示您需要了解的有關處理JavaScript錯誤的所有信息。
本文將引導您了解JavaScript中的基本錯誤,并解釋您可能遇到的各種錯誤。然后,您將學習如何識別和修復這些錯誤。還有一些技巧可以在生產(chǎn)環(huán)境中有效地處理錯誤。
什么是JavaScript錯誤?
編程錯誤是指程序無法正常運行的情況。當程序不知道如何處理手頭的工作時,可能會發(fā)生這種情況,例如嘗試打開不存在的文件或在沒有網(wǎng)絡連接的情況下訪問基于Web的API端點時。
這些情況促使程序向用戶拋出錯誤,說明它不知道如何繼續(xù)。該程序收集盡可能多的有關錯誤的信息,然后報告它無法繼續(xù)前進。
聰明的程序員試圖預測和覆蓋這些場景,這樣用戶就不必獨立地找出像“404”這樣的技術錯誤信息。相反,它們顯示了一條更容易理解的信息:“找不到該頁面?!?/p>
JavaScript中的錯誤是在發(fā)生編程錯誤時顯示的對象。這些對象包含有關錯誤類型、導致錯誤的語句以及發(fā)生錯誤時的堆棧跟蹤的大量信息。JavaScript還允許程序員創(chuàng)建自定義錯誤,以便在調(diào)試問題時提供額外信息。
錯誤的屬性
現(xiàn)在JavaScript錯誤的定義已經(jīng)很清楚了,是時候深入研究細節(jié)了。
JavaScript中的錯誤帶有某些標準和自定義屬性,有助于理解錯誤的原因和影響。默認情況下,JavaScript中的錯誤包含三個屬性:
此外,error還可以攜帶columnNumber、lineNumber、fileName等屬性,以更好地描述錯誤。但是,這些屬性不是標準的,可能會出現(xiàn)在JavaScript應用程序生成的每個錯誤對象中,也可能不會出現(xiàn)。
了解堆棧跟蹤
堆棧跟蹤是發(fā)生異?;蚓娴仁录r程序所在的方法調(diào)用列表。這是伴隨異常的示例堆棧跟蹤的樣子:
堆棧跟蹤示例
如您所見,它首先打印錯誤名稱和消息,然后是被調(diào)用的方法列表。每個方法調(diào)用都說明其源代碼的位置以及調(diào)用它的行。您可以使用這些數(shù)據(jù)瀏覽您的代碼庫并確定是哪段代碼導致了錯誤。
此方法列表以堆疊方式排列。它顯示了您的異常首次引發(fā)的位置以及它如何通過堆疊的方法調(diào)用傳播。為異常實現(xiàn)捕獲不會讓它通過堆棧向上傳播并使您的程序崩潰。但是,您可能希望在某些情況下故意不捕獲致命錯誤以使程序崩潰。
錯誤與異常
大多數(shù)人通常將錯誤和異常視為同一件事。但是,必須注意它們之間的細微但根本的區(qū)別。
異常是已拋出的錯誤對象。
為了更好地理解這一點,讓我們舉一個簡單的例子。以下是如何在JavaScript中定義錯誤:
const wrongTypeError = TypeError("Wrong type found, expected character")
這就是wrongTypeError對象變成異常的方式:
throw wrongTypeError
然而,大多數(shù)人傾向于使用在拋出錯誤對象時定義錯誤對象的簡寫形式:
throw TypeError("Wrong type found, expected character")
這是標準做法。然而,這也是開發(fā)人員傾向于混淆異常和錯誤的原因之一。因此,即使您使用速記來快速完成工作,了解基礎知識也至關重要。
JavaScript中的錯誤類型
JavaScript中有一系列預定義的錯誤類型。只要程序員沒有明確處理應用程序中的錯誤,它們就會由 JavaScript 運行時自動選擇和定義。
本節(jié)將引導您了解JavaScript中一些最常見的錯誤類型,并了解它們發(fā)生的時間和原因。
范圍錯誤
當變量設置為超出其合法值范圍時,會引發(fā)RangeError。它通常發(fā)生在將值作為參數(shù)傳遞給函數(shù)時,并且給定值不在函數(shù)參數(shù)的范圍內(nèi)。使用文檔記錄不佳的第三方庫時,有時修復起來會很棘手,因為您需要知道參數(shù)的可能值范圍才能傳遞正確的值。
RangeError發(fā)生的一些常見場景是:
- 試圖通過Array構造函數(shù)創(chuàng)建一個非法長度的數(shù)組。
- 將錯誤值傳遞給數(shù)字方法,如
toExponential(),toPrecision(),toFixed()等。 - 將非法值傳遞給字符串函數(shù),例如
normalize().
參考錯誤
當您的代碼中的變量引用出現(xiàn)問題時,就會發(fā)生ReferenceError。您可能忘記在使用變量之前為其定義值,或者您可能試圖在代碼中使用不可訪問的變量。在任何情況下,通過堆棧跟蹤提供了充足的信息來查找和修復有錯誤的變量引用。
ReferenceErrors發(fā)生的一些常見原因是:
- 在變量名中打錯字。
- 試圖訪問其范圍之外的塊范圍變量。
- 在加載之前從外部庫引用全局變量(例如 $ from jQuery)。
語法錯誤
這些錯誤是最容易修復的錯誤之一,因為它們表明代碼語法有錯誤。由于JavaScript是一種解釋而不是編譯的腳本語言,因此當應用程序執(zhí)行包含錯誤的腳本時會拋出這些腳本語言。在編譯語言的情況下,在編譯過程中會識別出此類錯誤。因此,在修復之前不會創(chuàng)建應用程序二進制文件。
可能發(fā)生SyntaxErrors的一些常見原因是:
- 缺少引號
- 缺少右括號
- 花括號或其他字符的不正確對齊
最好在IDE中使用linting工具在此類錯誤出現(xiàn)在瀏覽器之前為您識別這些錯誤。
類型錯誤
TypeError是JavaScript應用程序中最常見的錯誤之一。當某些值不是特定的預期類型時,會創(chuàng)建此錯誤。發(fā)生時的一些常見情況是:
- 調(diào)用不是方法的對象。
- 試圖訪問空對象或未定義對象的屬性
- 將字符串視為數(shù)字,反之亦然
發(fā)生TypeError的可能性還有很多。稍后我們將查看一些著名的實例并學習如何修復它們。
內(nèi)部錯誤
當JavaScript運行時引擎中發(fā)生異常時使用InternalError類型。它可能表示也可能不表示您的代碼存在問題。
通常,InternalError僅在兩種情況下發(fā)生:
- 當JavaScript運行時的補丁或更新帶有引發(fā)異常的錯誤時(這種情況很少發(fā)生)
- 當你的代碼包含對JavaScript引擎來說太大的實體時(例如太多的switch case、太大的數(shù)組初始化器、太多的遞歸)
解決此錯誤的最合適方法是通過錯誤消息確定原因,并在可能的情況下重構您的應用程序邏輯,以消除JavaScript引擎上的工作負載突然激增。
URI錯誤
URIError發(fā)生在全局URI處理函數(shù)如decodeURIComponent被非法使用時。它通常表示傳遞給方法調(diào)用的參數(shù)不符合URI標準,因此沒有被方法正確解析。
診斷這些錯誤通常很容易,因為您只需要檢查參數(shù)是否存在畸形。
評估錯誤
當函數(shù)eval()調(diào)用發(fā)生錯誤時會發(fā)生EvalError 。eval()函數(shù)用于執(zhí)行存儲在字符串中的 JavaScript 代碼。但是,由于安全問題,強烈建議不要使用eval()函數(shù),并且當前的ECMAScript規(guī)范不再拋出EvalError類,因此存在此錯誤類型只是為了保持與舊版JavaScript代碼的向后兼容性。
如果您使用的是舊版本的JavaScript,則可能會遇到此錯誤。無論如何,最好調(diào)查eval()函數(shù)調(diào)用中執(zhí)行的代碼是否有任何異常。
創(chuàng)建自定義錯誤類型
雖然JavaScript提供了足夠的錯誤類型列表來涵蓋大多數(shù)場景,但如果列表不滿足您的要求,您始終可以創(chuàng)建新的錯誤類型。這種靈活性的基礎在于JavaScript允許您使用throw命令逐字地拋出任何東西。
因此,從技術上講,這些聲明是完全合法的:
throw 8 throw "An error occurred"
但是,拋出原始數(shù)據(jù)類型不會提供有關錯誤的詳細信息,例如其類型、名稱或隨附的堆棧跟蹤。為了解決這個問題并標準化錯誤處理過程,我們提供了Error這個類。也不鼓勵在拋出異常時使用原始數(shù)據(jù)類型。
您可以擴展Error類以創(chuàng)建您的自定義錯誤類。以下是如何執(zhí)行此操作的基本示例:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
您可以通過以下方式使用它:
throw ValidationError("Property not found: name")
然后您可以使用instanceof關鍵字識別它:
try {
validateForm() // code that throws a ValidationError
} catch (e) {
if (e instanceof ValidationError)
// do something
else
// do something else
}
JavaScript中最常見的10個錯誤
現(xiàn)在您已經(jīng)了解了常見的錯誤類型以及如何創(chuàng)建自定義錯誤類型,是時候看看您在編寫JavaScript代碼時會遇到的一些最常見的錯誤了。
1. Uncaught RangeError
在幾種不同的情況下,Google Chrome中會出現(xiàn)此錯誤。首先,如果您調(diào)用遞歸函數(shù)并且它不會終止,則可能會發(fā)生這種情況。您可以在Chrome開發(fā)者控制臺中自行查看:
帶有遞歸函數(shù)調(diào)用的RangeError示例
因此,要解決此類錯誤,請確保正確定義遞歸函數(shù)的邊界情況。發(fā)生此錯誤的另一個原因是您傳遞的值超出了函數(shù)的參數(shù)范圍。這是一個例子:
帶有toExponential()調(diào)用的RangeError示例
錯誤消息通常會指出您的代碼有什么問題。一旦你做出改變,它就會得到解決。
toExponential() 函數(shù)調(diào)用的輸出
2. Uncaught TypeError: Cannot set property
當您在未定義的引用上設置屬性時會發(fā)生此錯誤。您可以使用此代碼重現(xiàn)該問題:
var list list.count = 0
這是您將收到的輸出:
類型錯誤示例
要修復此錯誤,請在訪問其屬性之前使用值初始化引用。以下是修復后的外觀:
如何修復類型錯誤
3. Uncaught TypeError: Cannot read property
這是JavaScript中最常出現(xiàn)的錯誤之一。當您嘗試讀取屬性或調(diào)用未定義對象的函數(shù)時,會發(fā)生此錯誤。您可以通過在Chrome開發(fā)人員控制臺中運行以下代碼來非常輕松地重現(xiàn)它:
var func func.call()
這是輸出:
帶有未定義函數(shù)的TypeError示例
未定義的對象是導致此錯誤的眾多可能原因之一。此問題的另一個突出原因可能是在呈現(xiàn) UI 時未正確初始化狀態(tài)。這是來自React應用程序的真實示例:
import React, { useState, useEffect } from "react";
const CardsList = () => {
const [state, setState] = useState();
useEffect(() => {
setTimeout(() => setState({ items: ["Card 1", "Card 2"] }), 2000);
}, []);
return (
<>
{state.items.map((item) => (
該應用程序從一個空狀態(tài)容器開始,并在延遲2秒后提供一些項目。延遲用于模擬網(wǎng)絡調(diào)用。即使您的網(wǎng)絡速度非??欤匀粫媾R輕微的延遲,因為該組件將至少呈現(xiàn)一次。如果您嘗試運行此應用程序,您將收到以下錯誤:
瀏覽器中的TypeError堆棧跟蹤
這是因為,在渲染時,狀態(tài)容器是未定義的;因此,它不存在任何財產(chǎn)items。修復這個錯誤很容易。您只需要為狀態(tài)容器提供初始默認值。
// ...
const [state, setState] = useState({items: []});
// ...
現(xiàn)在,在設置延遲之后,您的應用程序將顯示類似的輸出:
代碼輸出
代碼中的確切修復可能會有所不同,但這里的本質是始終在使用變量之前正確初始化它們。
4. TypeError: ‘undefined’ is not an object
當您嘗試訪問未定義對象的屬性或調(diào)用未定義對象的方法時,會在Safari中發(fā)生此錯誤。您可以從上面運行相同的代碼來自己重現(xiàn)錯誤。
帶有未定義函數(shù)的TypeError示例
這個錯誤的解決方法也是一樣的——確保你已經(jīng)正確地初始化了你的變量,并且在訪問一個屬性或方法時它們不是未定義的。
5. TypeError: null is not an object
這又與前面的錯誤相似。它發(fā)生在Safari上,這兩個錯誤之間的唯一區(qū)別是,當正在訪問其屬性或方法的對象null不是undefined. 您可以通過運行以下代碼來重現(xiàn)這一點:
var func = null func.call()
這是您將收到的輸出:
帶有null函數(shù)的TypeError示例
因為null是顯式設置為變量的值,而不是由JavaScript自動分配的值。僅當您嘗試訪問null自己設置的變量時,才會發(fā)生此錯誤。因此,您需要重新訪問您的代碼并檢查您編寫的邏輯是否正確。
6. TypeError: Cannot read property ‘length’
當您嘗試讀取null或undefined對象的長度時,Chrome中會出現(xiàn)此錯誤。這個問題的原因和前面的問題類似,但是在處理列表的時候出現(xiàn)的頻率比較高;因此值得特別提及。以下是重現(xiàn)問題的方法:
帶有未定義對象的TypeError示例
但是,在較新版本的Chrome中,此錯誤報告為Uncaught TypeError: Cannot read properties of undefined. 這是它現(xiàn)在的樣子:
在較新的Chrome版本上帶有未定義對象的TypeError示例
再次,修復是確保您嘗試訪問其長度的對象存在并且未設置為null.
7. TypeError: ‘undefined’ is not a function
當您嘗試調(diào)用腳本中不存在的方法或該方法存在但無法在調(diào)用上下文中引用時,會發(fā)生此錯誤。這個錯誤通常發(fā)生在谷歌瀏覽器中,您可以通過檢查拋出錯誤的代碼行來解決它。如果您發(fā)現(xiàn)拼寫錯誤,請修復它并檢查它是否能解決您的問題。
如果您在代碼中使用了自引用關鍵字this,如果this沒有適當?shù)亟壎ǖ侥纳舷挛?,則可能會出現(xiàn)此錯誤??紤]下面的代碼:
function showAlert() {
alert("message here")
}
document.addEventListener("click", () => {
this.showAlert();
})
如果執(zhí)行上述代碼,它將拋出我們討論過的錯誤。之所以會發(fā)生這種情況,是因為作為事件偵聽器傳遞的匿名函數(shù)正在document的上下文中執(zhí)行。
相比之下,showAlert函數(shù)是在window的上下文中定義的。
為了解決這個問題,您必須通過將函數(shù)與bind()方法綁定來傳遞對函數(shù)的正確引用:
document.addEventListener("click", this.showAlert.bind(this))
8. ReferenceError: event is not defined
當您嘗試訪問未在調(diào)用范圍內(nèi)定義的引用時,會發(fā)生此錯誤。這通常發(fā)生在處理事件時,因為它們經(jīng)常為您提供event回調(diào)函數(shù)中調(diào)用的引用。如果您忘記在函數(shù)的參數(shù)中定義事件參數(shù)或拼寫錯誤,則可能會發(fā)生此錯誤。
在Internet Explorer或Google Chrome中可能不會發(fā)生此錯誤(因為IE提供了一個全局事件變量,并且Chrome會自動將事件變量附加到處理程序),但它可能在Firefox中發(fā)生。所以建議留意這樣的小錯誤。
9. TypeError: Assignment to constant variable
這是由于粗心造成的錯誤。如果您嘗試將新值分配給常量變量,您將遇到這樣的結果:
帶有常量對象分配的TypeError示例
雖然現(xiàn)在看起來很容易修復,但想象一下數(shù)百個這樣的變量聲明,其中一個被錯誤地定義為const而不是let! 與PHP等其他腳本語言不同,在JavaScript中聲明常量和變量的風格差別很小。因此,當您遇到此錯誤時,建議首先檢查您的聲明。如果您忘記上述引用是一個常量并將其用作變量,您也可能會遇到此錯誤。這表明您的應用程序邏輯存在粗心或缺陷。嘗試解決此問題時,請務必檢查此項。
10.(unknown): Script error
當?shù)谌侥_本向您的瀏覽器發(fā)送錯誤時,就會發(fā)生腳本錯誤。此錯誤后跟(未知),因為第三方腳本與您的應用屬于不同的域。瀏覽器隱藏了其他細節(jié),以防止第三方腳本泄露敏感信息。
在不了解完整詳細信息的情況下,您無法解決此錯誤。您可以執(zhí)行以下操作來獲取有關該錯誤的更多信息:
一旦您可以訪問錯誤的詳細信息,您就可以著手解決問題,這可能與第三方庫或網(wǎng)絡有關。
如何識別和防止JavaScript中的錯誤
雖然上面討論的錯誤是JavaScript中最常見和最常見的錯誤,但您會遇到,僅僅依靠幾個示例是遠遠不夠的。在開發(fā)JavaScript應用程序時,了解如何檢測和防止任何類型的錯誤至關重要。以下是如何處理JavaScript中的錯誤。
手動拋出和捕獲錯誤
處理手動或運行時拋出的錯誤的最基本方法是捕獲它們。與大多數(shù)其他語言一樣,JavaScript提供了一組關鍵字來處理錯誤。在著手處理JavaScript應用程序中的錯誤之前,必須深入了解它們中的每一個。
throw
該集合的第一個也是最基本的關鍵字是throw. 很明顯,throw關鍵字用于拋出錯誤以在JavaScript運行時手動創(chuàng)建異常。我們已經(jīng)在本文前面討論過這個問題,這里是這個關鍵字意義的要點:
- 你可以
throw做任何事情,包括數(shù)字、字符串和Error對象。 - 但是,不建議拋出諸如字符串和數(shù)字之類的原始數(shù)據(jù)類型,因為它們不攜帶有關錯誤的調(diào)試信息。
- 例子:
throw TypeError("Please provide a string")
try
try關鍵字用于指示代碼塊可能會引發(fā)異常。它的語法是:
try {
// error-prone code here
}
重要的是要注意,catch塊必須始終跟隨try塊才能有效地處理錯誤。
catch
catch關鍵字用于創(chuàng)建一個catch塊。此代碼塊負責處理尾隨try塊捕獲的錯誤。這是它的語法:
catch (exception) {
// code to handle the exception here
}
這就是你如何一起實現(xiàn)try和catch塊的方式:
try {
// business logic code
} catch (exception) {
// error handling code
}
與C++或Java不同,您不能將多個catch塊附加到JavaScript中的try塊。這意味著您不能這樣做:
try {
// business logic code
} catch (exception) {
if (exception instanceof TypeError) {
// do something
}
} catch (exception) {
if (exception instanceof RangeError) {
// do something
}
}
相反,您可以在單個catch塊中使用if...else語句或switch case語句來處理所有可能的錯誤情況。它看起來像這樣:
try {
// business logic code
} catch (exception) {
if (exception instanceof TypeError) {
// do something
} else if (exception instanceof RangeError) {
// do something else
}
}
finally
finally關鍵字用于定義在處理錯誤后運行的代碼塊。該塊在try和catch塊之后執(zhí)行。
此外,無論其他兩個塊的結果如何,都會執(zhí)行finally塊。這意味著即使catch塊不能完全處理錯誤或者catch塊中拋出錯誤,解釋器也會在程序崩潰之前執(zhí)行finally塊中的代碼。
要被認為是有效的,JavaScript中的try塊需要后跟catch或finally塊。如果沒有這些,解釋器將引發(fā)SyntaxError。因此,在處理錯誤時,請確保至少遵循您的try塊。
使用onerror()方法全局處理錯誤
onerror()方法適用于所有HTML元素,用于處理它們可能發(fā)生的任何錯誤。例如,如果img標簽找不到指定URL的圖像,它會觸發(fā)其onerror方法以允許用戶處理錯誤。
通常,您會在onerror調(diào)用中提供另一個圖像URL,以便img標記回退到。這是您可以通過JavaScript執(zhí)行此操作的方法:
const image = document.querySelector("img")
image.onerror = (event) => {
console.log("Error occurred: " + event)
}
但是,您可以使用此功能為您的應用創(chuàng)建全局錯誤處理機制。以下是您的操作方法:
window.onerror = (event) => {
console.log("Error occurred: " + event)
}
使用此事件處理程序,您可以擺脫try...catch代碼中的多個塊,并集中您的應用程序的錯誤處理,類似于事件處理。您可以將多個錯誤處理程序附加到窗口,以維護SOLID設計原則中的單一責任原則。解釋器將循環(huán)遍歷所有處理程序,直到到達適當?shù)奶幚沓绦颉?/p>
通過回調(diào)傳遞錯誤
雖然簡單和線性函數(shù)允許錯誤處理保持簡單,但回調(diào)會使事情復雜化。
考慮以下代碼:
const calculateCube = (number, callback) => {
setTimeout(() => {
const cube = number * number * number
callback(cube)
}, 1000)
}
const callback = result => console.log(result)
calculateCube(4, callback)
上面的函數(shù)演示了一個異步條件,其中一個函數(shù)需要一些時間來處理操作并稍后在回調(diào)的幫助下返回結果。
如果您嘗試在函數(shù)調(diào)用中輸入字符串而不是4,您將得到NaN結果。
這需要妥善處理。就是這樣:
const calculateCube = (number, callback) => {
setTimeout(() => {
if (typeof number !== "number")
throw new Error("Numeric argument is expected")
const cube = number * number * number
callback(cube)
}, 1000)
}
const callback = result => console.log(result)
try {
calculateCube(4, callback)
} catch (e) { console.log(e) }
這應該可以理想地解決問題。但是,如果您嘗試將字符串傳遞給函數(shù)調(diào)用,您將收到以下信息:
錯誤參數(shù)的錯誤示例
即使您在調(diào)用函數(shù)時實現(xiàn)了try-catch塊,它仍然表示錯誤未捕獲。由于超時延遲,在執(zhí)行catch塊后拋出錯誤。
這可能在網(wǎng)絡調(diào)用中很快發(fā)生,在這種情況下會出現(xiàn)意外延遲。您需要在開發(fā)應用程序時涵蓋此類情況。
以下是在回調(diào)中正確處理錯誤的方法:
const calculateCube = (number, callback) => {
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)
}
try {
calculateCube('hey', callback)
} catch (e) {
console.log(e)
}
現(xiàn)在,控制臺的輸出將是:
帶有非法參數(shù)的TypeError示例
這表明錯誤已得到適當處理。
處理Promise中的錯誤
大多數(shù)人傾向于使用Promise來處理異步活動。Promise還有另一個優(yōu)點——被拒絕的Promise不會終止你的腳本。但是,您仍然需要實現(xiàn)一個catch塊來處理Promise中的錯誤。為了更好地理解這一點,讓我們使用Promises重寫calculateCube()函數(shù):
const delay = ms => new Promise(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
}
try {
calculateCube(4).then(r => console.log(r))
} catch (e) { console.log(e) }
前面代碼中的超時已被隔離到delay函數(shù)中以便理解。如果您嘗試輸入一個字符串而不是4,您獲得的輸出將類似于以下內(nèi)容:
在Promise中帶有非法參數(shù)的TypeError示例
同樣,這是由于Promise在其他所有內(nèi)容完成執(zhí)行后引發(fā)錯誤。這個問題的解決方案很簡單。只需像這樣向Promise鏈添加調(diào)用catch():
calculateCube("hey")
.then(r => console.log(r))
.catch(e => console.log(e))
現(xiàn)在輸出將是:
處理帶有非法參數(shù)的TypeError示例
您可以觀察到使用Promise處理錯誤是多么容易。此外,您可以鏈接finally()塊和promise調(diào)用以添加將在錯誤處理完成后運行的代碼。
或者,您也可以使用傳統(tǒng)的try-catch-finally技術來處理Promise中的錯誤。在這種情況下,您的promise調(diào)用如下所示:
try {
let result = await calculateCube("hey")
console.log(result)
} catch (e) {
console.log(e)
} finally {
console.log('Finally executed")
}
但是,這僅適用于異步函數(shù)。因此,在Promise中處理錯誤的最優(yōu)選方式是鏈式連接catch和finally連接到Promise調(diào)用。
throw/catch vs onerror() vs Callbacks vs Promises:哪種方法更佳?
有四種方法可供您使用,您必須知道如何在任何給定的用例中選擇最合適的方法。以下是您可以自己決定的方法:
throw/catch
您將在大多數(shù)情況下使用此方法。確保在你的catch塊中為所有可能的錯誤實現(xiàn)條件,如果你需要在try塊之后運行一些內(nèi)存清理例程,請記住包含一個finally塊。
但是,太多的try/catch塊會使您的代碼難以維護。如果您發(fā)現(xiàn)自己處于這種情況,您可能希望通過全局處理程序或promise方法來處理錯誤。
在異步try/catch塊和promise的catch()之間做出決定時,建議使用異步try/catch塊,因為它們將使您的代碼線性且易于調(diào)試。
onerror()
當您知道您的應用程序必須處理許多錯誤并且它們可以很好地分散在整個代碼庫中時,最好使用onerror()方法。onerror方法使您能夠處理錯誤,就好像它們只是您的應用程序處理的另一個事件一樣。您可以定義多個錯誤處理程序并在初始呈現(xiàn)時將它們附加到應用程序的窗口。
但是,您還必須記住,在錯誤范圍較小的較小項目中設置onerror()方法可能會帶來不必要的挑戰(zhàn)。如果您確定您的應用程序不會拋出太多錯誤,那么傳統(tǒng)的throw/catch方法將最適合您。
Callbacks and Promises
回調(diào)和承諾中的錯誤處理因代碼設計和結構而異。但是,如果您在編寫代碼之前在這兩者之間進行選擇,最好使用Promise。
這是因為Promise具有用于鏈接catch()和finally()塊以輕松處理錯誤的內(nèi)置結構。這種方法比定義附加參數(shù)/重用現(xiàn)有參數(shù)來處理錯誤更容易和更清晰。
使用Git存儲庫跟蹤更改
由于代碼庫中的手動錯誤,經(jīng)常會出現(xiàn)許多錯誤。在開發(fā)或調(diào)試代碼時,您最終可能會進行不必要的更改,這可能會導致代碼庫中出現(xiàn)新的錯誤。自動化測試是在每次更改后檢查代碼的好方法。但是,它只能告訴您是否有問題。如果你不經(jīng)常備份你的代碼,你最終會浪費時間試圖修復一個以前運行良好的函數(shù)或腳本。
這就是git發(fā)揮作用的地方。通過適當?shù)奶峤徊呗裕梢允褂媚膅it歷史作為備份系統(tǒng)來查看您的代碼在開發(fā)過程中的演變過程。您可以輕松地瀏覽舊提交并找出該函數(shù)的版本之前運行良好,但在不相關的更改后拋出錯誤。
然后,您可以恢復舊代碼或比較兩個版本以確定哪里出了問題?,F(xiàn)代Web開發(fā)工具(如GitHub Desktop或GitKraken)可幫助您并排可視化這些更改并快速找出錯誤。
一個可以幫助您減少錯誤的習慣是 在您對代碼進行重大更改時運行代碼審查。如果您在一個團隊中工作,您可以創(chuàng)建一個拉取請求并讓團隊成員徹底審查它。這將幫助您使用第二雙眼睛來發(fā)現(xiàn)您可能遺漏的任何錯誤。
處理JavaScript錯誤的最佳實踐
上述方法足以幫助您為下一個JavaScript應用程序設計一個健壯的錯誤處理方法。但是,最好在實施時記住一些事情,以充分利用您的防錯功能。這里有一些提示可以幫助您。
1. 處理操作異常時使用自定義錯誤
我們在本指南的前面介紹了自定義錯誤,讓您了解如何根據(jù)應用程序的獨特情況自定義錯誤處理。建議盡可能使用自定義錯誤而不是泛型Error類,因為它為調(diào)用環(huán)境提供了有關錯誤的更多上下文信息。
最重要的是,自定義錯誤允許您調(diào)整錯誤在調(diào)用環(huán)境中的顯示方式。這意味著您可以根據(jù)需要選擇隱藏特定詳細信息或顯示有關錯誤的其他信息。
您可以根據(jù)需要格式化錯誤內(nèi)容。這使您可以更好地控制錯誤的解釋和處理方式。
2.不要吞下任何例外
即使是最資深的開發(fā)人員也經(jīng)常犯一個新手錯誤——在他們的代碼中使用異常級別。
您可能會遇到有一段代碼可以選擇運行的情況。如果它有效,那就太好了;如果沒有,你不需要做任何事情。
在這些情況下,通常很想將這段代碼放在try塊中,并在其上附加一個空的catch塊。但是,通過這樣做,您將使那段代碼保持開放狀態(tài),從而導致任何類型的錯誤并逃脫懲罰。如果你有一個龐大的代碼庫和許多這樣糟糕的錯誤管理結構的實例,這可能會變得很危險。
處理異常的最好方法是確定所有異常都將被處理的級別并將它們提高到那里。此級別可以是控制器(在MVC架構應用程序中)或中間件(在傳統(tǒng)的面向服務器的應用程序中)。
通過這種方式,您將了解在哪里可以找到應用程序中發(fā)生的所有錯誤并選擇如何解決它們,即使這意味著不對它們做任何事情。
3. 對日志和錯誤警報使用集中策略
記錄錯誤通常是處理錯誤的一個組成部分。那些未能制定集中策略來記錄錯誤的人可能會錯過有關其應用程序使用情況的寶貴信息。
應用程序的事件日志可以幫助您找出有關錯誤的關鍵數(shù)據(jù)并幫助快速調(diào)試它們。如果您在應用程序中設置了適當?shù)木瘓髾C制,您可以在錯誤到達大部分用戶群之前知道應用程序何時發(fā)生錯誤。
建議使用預先構建的記錄器或創(chuàng)建一個以滿足您的需求。您可以配置此記錄器以根據(jù)其級別(警告、調(diào)試、信息等)處理錯誤,并且一些記錄器甚至可以立即將日志發(fā)送到遠程記錄服務器。通過這種方式,您可以觀察應用程序的邏輯在活動用戶中的執(zhí)行情況。
4. 適當?shù)赝ㄖ脩翦e誤
在定義錯誤處理策略時要牢記的另一個要點是牢記用戶。
所有干擾您的應用程序正常運行的錯誤都必須向用戶顯示可見的警報,以通知他們出現(xiàn)問題,以便用戶可以嘗試制定解決方案。如果您知道錯誤的快速修復方法,例如重試操作或注銷并重新登錄,請務必在警報中提及它以幫助實時修復用戶體驗。
如果錯誤不會對日常用戶體驗造成任何干擾,您可以考慮抑制警報并將錯誤記錄到遠程服務器以供以后解決。
5. 實現(xiàn)一個中間件(Node.js)
Node.js環(huán)境支持中間件向服務器應用程序添加功能。 您可以使用此功能為您的服務器創(chuàng)建錯誤處理中間件。
使用中間件的最大好處是所有錯誤都集中在一個地方處理。您可以輕松選擇啟用/禁用此設置以進行測試。
以下是創(chuàng)建基本中間件的方法:
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)
您現(xiàn)在可以在中間件中定義自定義邏輯以適當?shù)靥幚礤e誤。您不再需要擔心在整個代碼庫中實現(xiàn)單個錯誤處理結構。
6. 重新啟動您的應用程序以處理程序員錯誤(Node.js)
當Node.js應用程序遇到程序員錯誤時,它們可能不一定會拋出異常并嘗試關閉應用程序。此類錯誤可能包括由程序員錯誤引起的問題,例如高CPU消耗、內(nèi)存膨脹或內(nèi)存泄漏。處理這些問題的最佳方法是通過Node.js集群模式或PM2等獨特工具使應用程序崩潰,從而優(yōu)雅地重新啟動應用程序。這可以確保應用程序不會因用戶操作而崩潰,從而呈現(xiàn)糟糕的用戶體驗。
7. 捕獲所有未捕獲的異常(Node.js)
您永遠無法確定您已經(jīng)涵蓋了您的應用程序中可能出現(xiàn)的所有錯誤。因此,實施備用策略以從您的應用程序中捕獲所有未捕獲的異常非常重要。
您可以這樣做:
process.on('uncaughtException', error => {
console.log("ERROR: " + String(error))
// other handling mechanisms
})
您還可以確定發(fā)生的錯誤是標準異常還是自定義操作錯誤。根據(jù)結果??,您可以退出進程并重新啟動它以避免意外行為。
8. 捕獲所有未處理的Promise Rejections (Node.js)
與您永遠無法涵蓋所有??可能的異常類似,您很有可能會錯過處理所有可能的Promise Rejections。但是,與異常不同,Promise Rejection不會引發(fā)錯誤。
因此,一個重要的Promise Rejection可能會作為警告而溜走,并使您的應用程序面臨遇到意外行為的可能性。因此,實現(xiàn)一個回退機制來處理Promise Rejection是至關重要的。
您可以這樣做:
const promiseRejectionCallback = error => {
console.log("PROMISE REJECTED: " + String(error))
}
process.on('unhandledRejection', callback)
小結
與任何其他編程語言一樣,JavaScript中的錯誤非常頻繁且自然。在某些情況下,您甚至可能需要故意拋出錯誤以向用戶指示正確的響應。因此,了解它們的解剖結構和類型非常重要。
此外,您需要配備正確的工具和技術來識別和防止錯誤導致您的應用程序崩潰。
在大多數(shù)情況下,對于所有類型的JavaScript應用程序來說,通過仔細執(zhí)行來處理錯誤的可靠策略就足夠了。
新聞名稱:處理JavaScript錯誤的權威指南
網(wǎng)站地址:http://m.fisionsoft.com.cn/article/dghhhgj.html


咨詢
建站咨詢
