當JavaScript 引擎執行JavaScript 程式碼時,有可能會發生各種異常,例如是語法異常,語言中缺少的功能,由於來自伺服器或使用者的異常輸出而導致的異常。
而Javascript 引擎是單線程的,因此一旦遇到異常,Javascript 引擎通常會停止執行,阻塞後續程式碼並拋出一個異常訊息,因此對於可預見的異常,我們應該捕捉並正確展示給使用者或開發者。
throw 和Promise.reject() 可以拋出字串型別的例外,而且可以拋出一個 Error 物件類型的異常。
一個 Error 物件類型的異常不僅包含一個異常訊息,同時也包含一個追溯堆疊這樣你就可以很容易透過追溯堆疊找到程式碼出錯的行數了。
所以推薦拋出 Error 物件類型的例外,而不是字串類型的例外。
建立自己的異常建構子
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 expression;
throw 語句用來拋出一個使用者自訂的例外。目前函數的執行將被停止(throw 之後的語句將不會執行),並且控制將被傳遞到呼叫堆疊中的第一個 catch 區塊。如果呼叫者函數中沒有 catch 區塊,程式將會終止。
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 error
try { try_statements } [catch (exception) { catch_statements }] [finally { finally_statements }]
try/catch 主要用於捕捉例外。 try/catch 語句包含了一個try 區塊, 和至少有一個catch 區塊或一個finally 區塊,下面是三種形式的try 宣告:
try...catch
try...finally
#try...catch...finally
try 區塊中放入可能會產生例外的語句或函數
#catch 區塊中包含要執行的語句,當try 區塊中拋出異常時,catch 區塊會捕捉到這個例外訊息,並執行catch 區塊中的程式碼,如果在try 區塊中沒有異常拋出,這catch 區塊將會跳過。
finally 區塊在 try 區塊和 catch 區塊之後執行。無論是否有異常拋出或著是否被捕獲它總是執行。當在 finally 區塊中拋出異常訊息時會覆寫 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 區塊中傳回一個值,那麼這個值將會成為整個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()); // 3
有一個大家眾所周知的反最佳化模式就是使用 try/catch。
在V8(其他JS引擎也可能出現相同情況)函數中使用了 try/catch 語句不能夠被V8編譯器最佳化。參考http://www.html5rocks.com/en/tutorials/speed/v8/
透過在window.onerror 上定義一個事件監聽函數,程式中其他程式碼產生的未被捕獲的異常往往就會被window.onerror 上面註冊的監聽函數捕獲到。並且同時捕獲一些關於異常的資訊。
window.onerror = function (message, source, lineno, colno, error) { }
message
:異常訊息(字串)
##thsource#:發生異常的腳本URL(字串)
lineno:發生異常的行號(數字)
colno :發生異常的列號(數字)
error:Error物件(物件)
window.onerror 的回呼函數中使用第五個參數,也就是一個Error 物件並帶有一個追溯堆疊
try/catch 不能夠捕獲非同步程式碼中的異常,但是其會將異常拋向全域然後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 能夠偵測到從別的網域引用的script檔中的異常,並且將這些異常標記為Script error。如果你不想處理這些從別的域引入的script文件,那麼可以在程式中透過
Script error標記將其過濾掉。然而,在Firefox、Safari或IE11中,並不會引入跨域的JS異常,即使在Chrome中,如果使用
try/catch 將這些討厭的程式碼包圍,那麼Chrome也不會再偵測到這些跨域異常。
在Chrome中,如果你想通过 window.onerror 来获取到完整的跨域异常信息,那么这些跨域资源必须提供合适的跨域头信息。
new Promise((resolve,reject)=>{ reject(); })
Promise.resolve().then((resolve,reject)=>{ reject(); });
Promise.reject();
throw expression;
promiseObj.then(undefined, (err)=>{ catch_statements });
promiseObj.catch((exception)=>{ catch_statements })
在 JavaScript 函数中,只有 return / yield / throw 会中断函数的执行,其他的都无法阻止其运行到结束的。
在 resolve / reject 之前加上 return 能阻止往下继续运行。
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 error
无论是 try/catch 还是 promise 都能捕获到的是“同步”异常
reject 是回调,而 throw 只是一个同步的语句,如果在另一个异步的上下文中抛出,在当前上下文中是无法捕获到的。
因此在 Promise 中使用 reject 抛出异常。否则 catch 有可能会捕捉不到。
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 error
window.onunhandledrejection
与 window.onerror
类似,在一个JavaScript Promise 被 reject 但是没有 catch 来捕捉这个 reject时触发。并且同时捕获到一些关于异常的信息。
window.onunhandledrejection = event => { console.log(event.reason); }
event
事件是 PromiseRejectionEvent 的实例,它有两个属性:
event.promise
:被 rejected 的 JavaScript Promise
event.reason
:一个值或 Object 表明为什么 promise 被 rejected,是 Promise.reject() 中的内容。
因为 Promise 可以延后调用 catch 方法,若在抛出 reject 时未调用 catch 进行捕捉,但稍后再次调用 catch,此时会触发 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
代码中抛出的异常,一种是要展示给用户,一种是展示给开发者。
对于展示给用户的异常,一般使用 alert 或 toast 展示;对于展示给开发者的异常,一般输出到控制台。
在一个函数或一个代码块中可以把抛出的异常统一捕捉起来,按照不同的异常类型以不同的方式展示,对于。
需要点击确认的异常类型:
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;
提示开发者的异常类型:
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;
异常处理器:
抛出普通异常时,可以带上 stackoverflow 上问题的列表,方便开发者查找原因。
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;
以上是JavaScript的異常處理的解釋的詳細內容。更多資訊請關注PHP中文網其他相關文章!