JavaScript
原生提供了7種錯誤類型,分別是 Error
, EvalError
, SyntaxError
, RangeError
, ReferenceError
, TypeError
, 和 URIError
。當咱們在編寫提供給其餘開發者使用的庫(包)時,爲了在必要的時候給予開發者錯誤信息反饋(例如,開發者所傳參數類型不正確),咱們一般會拋出錯誤,可是原生提供的這些錯誤類型不夠有針對性,不能讓開發者一眼就能看出是咱們的庫所拋出的錯誤,因此咱們須要定製本身的錯誤類型,當開發者錯誤使用庫而且運行代碼時可以在控制檯看到咱們所定義的錯誤類型,例如:javascript
CustomError: something bad happened.
而不是 :java
Error: something bad happened.
另外,咱們能夠爲本身的錯誤類型添加額外的信息字段,原生提供的錯誤只有 name
(錯誤類型),message
(錯誤信息),stack
(部分js環境提供堆棧追蹤信息),這些字段可能不可以知足咱們的需求,咱們想給咱們所拋出的錯誤添加一個 reason
字段來代表拋出錯誤的緣由,那麼咱們就須要自定義錯誤類型。app
Error
類咱們自定義的錯誤類型最好繼承自Error
類,但這並非強制規定,這只是一種良好的代碼習慣,由於當開發者經過 try / catch
捕獲到異常時,可能會進行進一步的判斷:函數
if (error instanceof Error) { ... }
這樣咱們的自定義錯誤才能被有效捕獲。測試
ES5
自定義錯誤的寫法function CustomError(message) { // 實例化自定義錯誤時所傳的錯誤信息參數 this.message = message // name 指明該錯誤類型(同時在控制檯所打印的錯誤類型即由此字段指明),不指明默認爲Error。 this.name = 'CustomError' // 捕獲到當前執行環境的堆棧追蹤信息,爲自定義錯誤實例添加 `stack` 字段進行保存, // 第二個參數的含義爲:堆棧追蹤只會展現到`CustomError`這個函數(即自定義錯誤的構造函數)被調用以前。 Error.captureStackTrace(this, CustomError) } // 原型鏈繼承 (詳見 JS高程 一書) CustomError.prototype = new Error // 在自定義錯誤的原型上添加構造器函數爲CustomError, // 若不添加構造器,當獲取自定義錯誤的構造器時,獲取的是上一步`new Error`實例的原型的構造器,即`Error`構造函數 CustomError.prototype.constructor = CustomError
ES6
自定義錯誤的寫法ES6 添加了 class 這一語法糖,能夠方便的寫出繼承關係:this
class CustomError extends Error { constructor (message) { super(message) this.name = 'CustomError', // 這一步可不寫,默認會保存堆棧追蹤信息到自定義錯誤構造函數以前, // 而若是寫成 `Error.captureStackTrace(this)` 則自定義錯誤的構造函數也會被保存到堆棧追蹤信息 Error.captureStackTrace(this, this.constructor) } }
接下來咱們經過ES 5/6 兩種不一樣的寫法來自定義錯誤類型後, 使用下面的代碼在 Node.js
中進行測試。咱們先推測堆棧追蹤自棧頂向下依次應該是:prototype
at new CustomError // 若堆棧追蹤到自定義錯誤的構造函數時,會有此打印 at c at b at a
測試代碼:code
// `c` 函數拋出自定義錯誤 function c() { throw new CustomError('something bad happened!') } // `b` 函數調用 `c` function b() { c() } // `a` 函數調用 `b` function a() { b() } try { a() // 執行 `a` 函數 } catch (err) { // 判斷所拋出的錯誤是否爲 `Error` 的子類實例 if (err instanceof Error) { console.log('Capture the error:') console.log(err.stack) // 打印堆棧追蹤信息 console.log(err instanceof CustomError, err instanceof Error) // 錯誤是否爲(或繼承自)CustomError 和 Error 類型 console.log(err.constructor.name) // 自定義錯誤的構造器名稱 } }
對比ES 5/6 兩種自定義錯誤的方式,運行結果均爲:繼承
Capture the error: CustomError: something bad happened! at c (/Users/xavier/Documents/demos/javascript-demo/test.js:19:8) at b (/Users/xavier/Documents/demos/javascript-demo/test.js:23:2) at a (/Users/xavier/Documents/demos/javascript-demo/test.js:27:2) at Object.<anonymous> (/Users/xavier/Documents/demos/javascript-demo/test.js:31:2) at Module._compile (internal/modules/cjs/loader.js:689:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) at Module.load (internal/modules/cjs/loader.js:599:32) at tryModuleLoad (internal/modules/cjs/loader.js:538:12) at Function.Module._load (internal/modules/cjs/loader.js:530:3) at Function.Module.runMain (internal/modules/cjs/loader.js:742:12) true true CustomError
測試說明兩種自定義錯誤的方式效果相同,ES6 class
寫法雖然方便,但只是語法糖,ES5
的寫法有利於咱們理解 JavaScript
基於原型的繼承方式,二者各有利弊。ip