摘要:主要用于捕捉異常。這包括在塊里拋出的異常。并且同時(shí)捕獲到一些關(guān)于異常的信息。秒后輸出統(tǒng)一異常處理代碼中拋出的異常,一種是要展示給用戶,一種是展示給開發(fā)者。
當(dāng) JavaScript 引擎執(zhí)行 JavaScript 代碼時(shí),有可能會(huì)發(fā)生各種異常,例如是語法異常,語言中缺少的功能,由于來自服務(wù)器或用戶的異常輸出而導(dǎo)致的異常。
而 Javascript 引擎是單線程的,因此一旦遇到異常,Javascript 引擎通常會(huì)停止執(zhí)行,阻塞后續(xù)代碼并拋出一個(gè)異常信息,因此對(duì)于可預(yù)見的異常,我們應(yīng)該捕捉并正確展示給用戶或開發(fā)者。
Error對(duì)象throw 和 Promise.reject() 可以拋出字符串類型的異常,而且可以拋出一個(gè) Error 對(duì)象類型的異常。
一個(gè) Error 對(duì)象類型的異常不僅包含一個(gè)異常信息,同時(shí)也包含一個(gè)追溯棧這樣你就可以很容易通過追溯棧找到代碼出錯(cuò)的行數(shù)了。
所以推薦拋出 Error 對(duì)象類型的異常,而不是字符串類型的異常。
創(chuàng)建自己的異常構(gòu)造函數(shù)
function MyError(message) { var instance = new Error(message); instance.name = "MyError"; Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); return instance; } MyError.prototype = Object.create(Error.prototype, { constructor: { value: MyError, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf) { Object.setPrototypeOf(MyError, Error); } else { MyError.__proto__ = Error; } export default MyError;
在代碼中拋出自定義的異常類型并捕捉
try { throw new MyError("some message"); } catch(e){ console.log(e.name + ":" + e.message); }Throw
throw expression;
throw 語句用來拋出一個(gè)用戶自定義的異常。當(dāng)前函數(shù)的執(zhí)行將被停止(throw 之后的語句將不會(huì)執(zhí)行),并且控制將被傳遞到調(diào)用堆棧中的第一個(gè) catch 塊。如果調(diào)用者函數(shù)中沒有 catch 塊,程序?qū)?huì)終止。
try { console.log("before throw error"); throw new Error("throw error"); console.log("after throw error"); } catch (err) { console.log(err.message); } // before throw error // throw errorTry / Catch
try { try_statements } [catch (exception) { catch_statements }] [finally { finally_statements }]
try/catch 主要用于捕捉異常。try/catch 語句包含了一個(gè) try 塊, 和至少有一個(gè) catch 塊或者一個(gè) finally 塊,下面是三種形式的 try 聲明:
try...catch
try...finally
try...catch...finally
try 塊中放入可能會(huì)產(chǎn)生異常的語句或函數(shù)
catch 塊中包含要執(zhí)行的語句,當(dāng) try 塊中拋出異常時(shí),catch 塊會(huì)捕捉到這個(gè)異常信息,并執(zhí)行 catch 塊中的代碼,如果在 try 塊中沒有異常拋出,這 catch 塊將會(huì)跳過。
finally 塊在 try 塊和 catch 塊之后執(zhí)行。無論是否有異常拋出或著是否被捕獲它總是執(zhí)行。當(dāng)在 finally 塊中拋出異常信息時(shí)會(huì)覆蓋掉 try 塊中的異常信息。
try { try { throw new Error("can not find it1"); } finally { throw new Error("can not find it2"); } } catch (err) { console.log(err.message); } // can not find it2
如果從 finally 塊中返回一個(gè)值,那么這個(gè)值將會(huì)成為整個(gè) try-catch-finally 的返回值,無論是否有 return 語句在 try 和 catch 中。這包括在 catch 塊里拋出的異常。
function test() { try { throw new Error("can not find it1"); return 1; } catch (err) { throw new Error("can not find it2"); return 2; } finally { return 3; } } console.log(test()); // 3Try / Catch 性能
有一個(gè)大家眾所周知的反優(yōu)化模式就是使用 try/catch。
在V8(其他JS引擎也可能出現(xiàn)相同情況)函數(shù)中使用了 try/catch 語句不能夠被V8編譯器優(yōu)化。參考http://www.html5rocks.com/en/tutorials/speed/v8/
window.onerror通過在 window.onerror 上定義一個(gè)事件監(jiān)聽函數(shù),程序中其他代碼產(chǎn)生的未被捕獲的異常往往就會(huì)被 window.onerror 上面注冊(cè)的監(jiān)聽函數(shù)捕獲到。并且同時(shí)捕獲到一些關(guān)于異常的信息。
window.onerror = function (message, source, lineno, colno, error) { }
message:異常信息(字符串)
source:發(fā)生異常的腳本URL(字符串)
lineno:發(fā)生異常的行號(hào)(數(shù)字)
colno:發(fā)生異常的列號(hào)(數(shù)字)
error:Error對(duì)象(對(duì)象)
注意:Safari 和 IE10 還不支持在 window.onerror 的回調(diào)函數(shù)中使用第五個(gè)參數(shù),也就是一個(gè) Error 對(duì)象并帶有一個(gè)追溯棧
try/catch 不能夠捕獲異步代碼中的異常,但是其將會(huì)把異常拋向全局然后 window.onerror 可以將其捕獲。
try { setTimeout(() => { throw new Error("some message"); }, 0); } catch (err) { console.log(err); } // Uncaught Error: some message
window.onerror = (msg, url, line, col, err) => { console.log(err); } setTimeout(() => { throw new Error("some message"); }, 0); // Error: some message
在Chrome中,window.onerror 能夠檢測(cè)到從別的域引用的script文件中的異常,并且將這些異常標(biāo)記為Script error。如果你不想處理這些從別的域引入的script文件,那么可以在程序中通過Script error標(biāo)記將其過濾掉。然而,在Firefox、Safari或者IE11中,并不會(huì)引入跨域的JS異常,即使在Chrome中,如果使用 try/catch 將這些討厭的代碼包圍,那么Chrome也不會(huì)再檢測(cè)到這些跨域異常。
在Chrome中,如果你想通過 window.onerror 來獲取到完整的跨域異常信息,那么這些跨域資源必須提供合適的跨域頭信息。
Promise中的異常 Promise中拋出異常new Promise((resolve,reject)=>{ reject(); })
Promise.resolve().then((resolve,reject)=>{ reject(); });
Promise.reject();
throw expression;Promise中捕捉異常
promiseObj.then(undefined, (err)=>{ catch_statements });
promiseObj.catch((exception)=>{ catch_statements })
在 JavaScript 函數(shù)中,只有 return / yield / throw 會(huì)中斷函數(shù)的執(zhí)行,其他的都無法阻止其運(yùn)行到結(jié)束的。
在 resolve / reject 之前加上 return 能阻止往下繼續(xù)運(yùn)行。
without return:
Promise.resolve() .then(() => { console.log("before excute reject"); reject(new Error("throw error")); console.log("after excute reject"); }) .catch((err) => { console.log(err.message); }); // before excute reject // throw error // after excute reject
use return:
Promise.resolve() .then(() => { console.log("before excute reject"); return reject(new Error("throw error")); console.log("after excute reject"); }) .catch((err) => { console.log(err.message); }); // before excute reject // throw errorThrow or Reject
無論是 try/catch 還是 promise 都能捕獲到的是“同步”異常
reject 是回調(diào),而 throw 只是一個(gè)同步的語句,如果在另一個(gè)異步的上下文中拋出,在當(dāng)前上下文中是無法捕獲到的。
因此在 Promise 中使用 reject 拋出異常。否則 catch 有可能會(huì)捕捉不到。
Promise.resolve() .then(() => { setTimeout(()=>{ throw new Error("throw error"); },0); }) .catch((err) => { console.log(err); }); // Uncaught Error: throw error
Promise.resolve() .then(() => { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error("throw error")); }, 0); }); }) .catch((err) => { console.log(err); }); // Error: throw errorwindow.onunhandledrejection
window.onunhandledrejection 與 window.onerror 類似,在一個(gè)JavaScript Promise 被 reject 但是沒有 catch 來捕捉這個(gè) reject時(shí)觸發(fā)。并且同時(shí)捕獲到一些關(guān)于異常的信息。
window.onunhandledrejection = event => { console.log(event.reason); }
event事件是 PromiseRejectionEvent 的實(shí)例,它有兩個(gè)屬性:
event.promise:被 rejected 的 JavaScript Promise
event.reason:一個(gè)值或 Object 表明為什么 promise 被 rejected,是 Promise.reject() 中的內(nèi)容。
window.rejectionhandled因?yàn)?Promise 可以延后調(diào)用 catch 方法,若在拋出 reject 時(shí)未調(diào)用 catch 進(jìn)行捕捉,但稍后再次調(diào)用 catch,此時(shí)會(huì)觸發(fā) rejectionhandled 事件。
window.onrejectionhandled = event => { console.log("rejection handled"); } let p = Promise.reject(new Error("throw error")); setTimeout(()=>{ p.catch(e=>{console.log(e)}); },1000); // Uncaught (in promise) Error: throw error // 1秒后輸出 // Error: throw error // rejection handled統(tǒng)一異常處理
代碼中拋出的異常,一種是要展示給用戶,一種是展示給開發(fā)者。
對(duì)于展示給用戶的異常,一般使用 alert 或 toast 展示;對(duì)于展示給開發(fā)者的異常,一般輸出到控制臺(tái)。
在一個(gè)函數(shù)或一個(gè)代碼塊中可以把拋出的異常統(tǒng)一捕捉起來,按照不同的異常類型以不同的方式展示,對(duì)于。
需要點(diǎn)擊確認(rèn)的異常類型:
ensureError.js
function EnsureError(message = "Default Message") { this.name = "EnsureError"; this.message = message; this.stack = (new Error()).stack; } EnsureError.prototype = Object.create(Error.prototype); EnsureError.prototype.constructor = EnsureError; export default EnsureError;
彈窗提示的異常類型:
toastError.js
function ToastError(message = "Default Message") { this.name = "ToastError"; this.message = message; this.stack = (new Error()).stack; } ToastError.prototype = Object.create(Error.prototype); ToastError.prototype.constructor = ToastError; export default ToastError;
提示開發(fā)者的異常類型:
devError.js
function DevError(message = "Default Message") { this.name = "ToastError"; this.message = message; this.stack = (new Error()).stack; } DevError.prototype = Object.create(Error.prototype); DevError.prototype.constructor = DevError; export default DevError;
異常處理器:
拋出普通異常時(shí),可以帶上 stackoverflow 上問題的列表,方便開發(fā)者查找原因。
errorHandler.js
import EnsureError from "./ensureError.js"; import ToastError from "./toastError.js"; import DevError from "./devError.js"; import EnsurePopup from "./ensurePopup.js"; import ToastPopup from "./toastPopup.js"; function errorHandler(err) { if (err instanceof EnsureError) { EnsurePopup(err.message); } else if (err instanceof ToastError) { ToastPopup(err.message); }else if( err instanceof DevError){ DevError(err.message); }else{ error.message += ` https://stackoverflow.com/questions?q=${encodeURI(error.message)}` console.error(err.message); } } window.onerror = (msg, url, line, col, err) => { errorHandler(err); } window.onunhandledrejection = event =>{ errorHandler(event.reason); }; export default errorHandler;
歡迎關(guān)注:Leechikit
原文鏈接:segmentfault.com到此本文結(jié)束,歡迎提問和指正。
寫原創(chuàng)文章不易,若本文對(duì)你有幫助,請(qǐng)點(diǎn)贊、推薦和關(guān)注作者支持。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88927.html
摘要:但如果忽視異常輕則影響功能運(yùn)行,重則導(dǎo)致系統(tǒng)崩潰,造成經(jīng)濟(jì)損失。異常處理捕獲異常捕獲關(guān)鍵字與一致,都是使用。語句塊表示無論是否發(fā)生異常,語句塊代碼一定會(huì)被執(zhí)行。 不少前端工程師看到這個(gè)標(biāo)題可能會(huì)產(chǎn)生質(zhì)問: 我js用得好好的,能后端能APP,為什么還要學(xué)習(xí)Python? 至少有下面兩個(gè)理由: 學(xué)習(xí)曲線。ES6之后的JavaScript(TypeScript)的在語法上和Python有很...
摘要:單元測(cè)試會(huì)體現(xiàn)出以上錯(cuò)誤處理程序的作用如果出現(xiàn)問題,錯(cuò)誤處理程序就會(huì)返回。同時(shí)錯(cuò)誤會(huì)展開堆棧,這對(duì)調(diào)試非常有幫助。展開堆棧處理異常的一種方式是在調(diào)用堆棧的頂部加入。確保你的錯(cuò)誤處理處在相同域中,這樣會(huì)保留原始消息,堆棧和自定義錯(cuò)誤對(duì)象。 JavaScript的事件驅(qū)動(dòng)范式增添了豐富的語言,也是讓使用JavaScript編程變得更加多樣化。如果將瀏覽器設(shè)想為JavaScript的事件驅(qū)動(dòng)...
摘要:幸運(yùn)的是,使用符號(hào)創(chuàng)建的構(gòu)造器,如果在不使用來調(diào)用,則始終會(huì)報(bào)錯(cuò),即使在非嚴(yán)格模式下也不會(huì)產(chǎn)生問題。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:Bugs and Errors 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分參考了《JavaScript 編程精解(第 2 版)》 調(diào)試的難度是開始編寫代碼的兩倍。 因此,如...
摘要:我們使用單元測(cè)試來驗(yàn)證一下我們使用了配合做單元測(cè)試。我們編寫相應(yīng)的單元測(cè)試你會(huì)發(fā)現(xiàn),如果出現(xiàn)異常,只是簡單的返回。但是在上面異常拋出的時(shí)候,解釋器已經(jīng)不在中了,因此無法被捕獲。 譯者按: 錯(cuò)誤是無法避免的,妥善處理它才是最重要的! 原文: A Guide to Proper Error Handling in JavaScript Related Topics: 譯者: Funde...
摘要:會(huì)自動(dòng)調(diào)用轉(zhuǎn)換函數(shù)將這個(gè)表達(dá)式的結(jié)果轉(zhuǎn)換為一個(gè)布爾值。語句語句與語句的關(guān)系最為密切,而且也是在其他語言中普遍使用的一種流控制語句。 表達(dá)式在 JavaScript 中是短語,那么語句就是整句命令。表達(dá)式用來計(jì)算出一個(gè)值,語句用來執(zhí)行以使某件事發(fā)生。從本質(zhì)上看,語句定義了 JavaScript 中的主要語法,語句通常使用一或多個(gè)關(guān)鍵字來完成給定任務(wù)。語句可以很簡單,例如通知函數(shù)退出;也可...
閱讀 2480·2021-10-12 10:11
閱讀 1229·2021-10-11 10:58
閱讀 3274·2019-08-30 15:54
閱讀 712·2019-08-30 13:59
閱讀 680·2019-08-29 13:07
閱讀 1407·2019-08-26 11:55
閱讀 2142·2019-08-26 10:44
閱讀 2642·2019-08-23 18:25