簡單了解TypeScript中如何繼承 Error 類

 更新時間:2019年06月21日 17:03:56   作者:Harttle   我要評論
這篇文章主要介紹了簡單了解TypeScript中如何繼承 Error 類,一個典型的網站服務器可能需要有 NetworkError, DatabaseError, UnauthorizedError 等。 我們希望這些類都擁有 Error 的特性:有錯誤消息、有調用棧、有方便打印的 toString 等。,需要的朋友可以參考下

前言

在JavaScript 中很多時候都需要自定義錯誤,尤其是開發 Node.js 應用的時候。 比如一個典型的網站服務器可能需要有 NetworkError, DatabaseError, UnauthorizedError 等。 我們希望這些類都擁有 Error 的特性:有錯誤消息、有調用棧、有方便打印的 toString 等。 最直觀的實現方式便是 繼承 Error 類。 但考慮 TypeScript 需要編譯到 ES5 兼容性問題會較為復雜, 本文用來幫助理解 TypeScript 中繼承 Error 的問題來源以及對應的幾種解決方式。

我們需要怎樣的 CustomError

為了容易討論最佳實踐,首先明確我們自定義的 CustomError 需要做到哪些功能。 下面是 Harttle 的觀點:

  1. 可以調用 new CustomError() 來創建,并且 instanceof Error 操作應該返回 true。可以用來創建是基本要求,能夠被視為 Error 的實例能夠兼容既有系統(比如 toString() 要返回調用棧),同時符合慣例。
  2. .stack 屬性首行應為 CustomeError: <message>。如果是 Error: <message> 可能就沒那么漂亮。
  3. .stack 屬性應當包含調用棧并指向 new CustomError() 的那一行。這一點可能是關鍵,如果指向 CustomError 構造函數中的某一行,就會給這個類的使用方造成困惑。

下面舉個例子,這是一個 message 為 "intended" 的 CustomError 的 .stack 屬性值:

CustomError: intended
at Object.<anonymous> (/Users/harttle/Downloads/bar/a.js:10:13)
at Module._compile (module.js:653:30)
at Object.Module._extensions..js (module.js:664:10)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)
at Function.Module._load (module.js:498:3)
at Function.Module.runMain (module.js:694:10)
at startup (bootstrap_node.js:204:16)
at bootstrap_node.js:625:3

ES5 中如何繼承 Error?

Error 是一個特殊的對象,或者說 JavaScript 的 new 是一個奇葩的存在。 為方便后續討論,我們先討論組 ES5 時代是怎樣繼承 Error 的。 我們說 JavaScript 是一門混雜的語言,如何繼承 Error 就是一個典型的例子。 如果你熟悉 原型繼承的方式,應該會寫出如下代碼:

function CustomError (message) {
Error.call(this, message)
}
CustomError.prototype = new Error()

因為 stack 只在 new 的時候生成,上述實現不能滿足功能 2 和功能 3,也就是說:

  • stack 的第一行是總是 Error 而不是 CustomError 且不包含 message 信息。
  • stack 總是指向 new Error() 的那一行,而不是 new CustomError()。

Node 文檔 中描述了一個 captureStackTrace 方法來解決這個問題,改動后的實現如下:

function CustomError (msg) {
this.name = 'CustomError'
this.message = msg
Error.captureStackTrace(this, CustomError)
}
CustomError.prototype = new Error()

其中 .captureStackTrace() 會使用傳入對象的 name 和 message 來生成 stack 的前綴;同時第二個參數用來指定在調用棧中忽略掉哪一部分,這樣棧就會指向 new CustomError 的地方而不是 captureStackTrace() 的地方。

ES6 中如何繼承 Error?

既然 ES6 通過 class 和 extends 等關鍵字給出了類繼承機制, 那么想必通過編寫 CustomError 類來繼承 Error。事實也確實如此,只需要在構造函數中調用父類構造函數并賦值 name 即可實現文章開始提到的三個功能:

class CustomError extends Error {
constructor(msg) {
super(msg)
this.name = 'CustomError'
}
}

TypeScript 中如何繼承 Error?

ES6 中提供了 new.target 屬性, 使得 Error 的構造函數中可以獲取 CustomError 的信息,以完成原型鏈的調整。 因此 TypeScript 需要編譯到 ES5 時上述功能仍然是無法自動實現。 在 TypeScript 中的體現是形如上述 ES6 的代碼片段會被編譯成:

var CustomError = /** @class */ (function (_super) {
__extends(CustomError, _super);
function CustomError(msg) {
var _this = _super.call(this, msg) || this;
_this.name = 'CustomError';
return _this;
}
return CustomError;
}(Error));

注意 var _this = _super.call(this, msg) || this; 中 this 被替換掉了。 在 TypeScript 2.1 的 changelog 中描述了這個 Breaking Change。 **這會造成 CustomError 的所有對象方法都無法使用,這里介紹幾種 workaround:

題外話,這個分支可能會導致測試覆蓋率中的 分支未覆蓋問題。可以只在 ES6 下產生測試覆蓋報告來解決。

1. 使用 setPrototypeOf 還原原型鏈

這是 TypeScript 官方給出的解決方法,見這里

class CustomError extends Error {
constructor(message) {
super(message);
Object.setPrototypeOf(this, FooError.prototype);
}
}

注意這是一個性能很差的方法,且在 ES6 中提出,兼容性也很差。在不兼容的環境下可以使用 __proto__ 來替代。

2. 堅持使用 ES5 的方式

不使用 ES6 特性,仍然使用本文前面介紹的 『ES5 中如何繼承 Error?』給出的方法。

3. 限制對象方法的使用

雖然 CustomError 的對象函數無法使用,但 CustomError 仍然支持 protected 級別的方法供子類使用,閹割的地方在于自己不能調用。 由于 JavaScript 中對象屬性必須在構造函數內賦值,因此對象屬性也不會受到影響。也就是說:

class CustomError extends Error {
count: number = 0
constructor(msg) {
super(msg)
this.count // OK,屬性不受影響
this.print() // TypeError: _this.print is not a function,因為 this 被替換了
}
print() { 
console.log(this.stack)
}
}
class DerivedError extends CustomError {
constructor(msg) {
super(msg)
super.print() // OK,因為 print 是直接從父類原型獲取的,即 `_super.prototype.print`
}
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • JavaScript使用canvas繪制隨機驗證碼

    JavaScript使用canvas繪制隨機驗證碼

    這篇文章主要為大家詳細介紹了JavaScript使用canvas繪制隨機驗證碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 詳解js模板引擎art template數組渲染的方法

    詳解js模板引擎art template數組渲染的方法

    art-template 是一個簡約、超快的模板引擎。這篇文章主要介紹了詳解js模板引擎art template數組渲染的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-10-10
  • JavaScript中的遍歷詳解(多種遍歷)

    JavaScript中的遍歷詳解(多種遍歷)

    編程這么多年,要是每次寫遍歷代碼時都用 for 循環,真心感覺對不起 JavaScript 語言,這篇文章為大家分享幾種不同的遍歷方法,需要的朋友可以參考下
    2017-04-04
  • JavaScript從0開始構思表情插件

    JavaScript從0開始構思表情插件

    在項目開發階段很多項目都會用到表情插件,接下來通過本文給大家介紹了JavaScript從0開始構思表情插件 的相關資料,需要的朋友可以參考下
    2016-07-07
  • JavaScript面試技巧之數組的一些不low操作

    JavaScript面試技巧之數組的一些不low操作

    這篇文章主要給大家介紹了關于JavaScript面試技巧之數組的一些不low操作的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用js具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-03-03
  • JavaScript 瀏覽器兼容性總結及常用瀏覽器兼容性分析

    JavaScript 瀏覽器兼容性總結及常用瀏覽器兼容性分析

    本文是腳本之家小編日常整理些關于js兼容性問題,及IE與Firefox等常用瀏覽器的兼容性分析,對js瀏覽器兼容性相關知識感興趣的朋友一起學習吧
    2016-03-03
  • js a標簽點擊事件

    js a標簽點擊事件

    本文將詳細介紹a標簽上的點擊事件的相關知識。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-03-03
  • Javascript從數組中隨機取出不同元素的兩種方法

    Javascript從數組中隨機取出不同元素的兩種方法

    這篇文章給大家分享了兩種Javascript從數組中隨機取出不同元素的方法,大家可以都學習學習,這樣更能有助于大家的學習和理解,下面來一起看看吧
    2016-09-09
  • 一步步教會你微信小程序的登錄鑒權

    一步步教會你微信小程序的登錄鑒權

    登錄功能是許多小程序必備的一個功能,通過登錄系統,我們可以記錄用戶在我們的小程序里一些行為,在后臺我們也可以模糊地確認用戶。下面這篇文章主要給大家介紹了關于小程序登錄鑒權的相關資料,需要的朋友可以參考下。
    2018-04-04
  • 高性能JavaScript模板引擎實現原理詳解

    高性能JavaScript模板引擎實現原理詳解

    這篇文章主要介紹了JavaScript模板引擎實現原理詳解,本文著重講解artTemplate模板的實現原理,它采用預編譯方式讓性能有了質的飛躍,是其它知名模板引擎的25、32 倍,需要的朋友可以參考下
    2015-02-02

最新評論

湖北11选5走势图爱彩乐 114百家乐 JJ大众麻将技巧 新疆时时彩几点到几点 广东十分快乐走势图 四肖期期中特精选资料 大嘴刨大王官方下载下载 浙江体彩20选5杀号 王中王鉄算盘开奖结果小说 星星武汉麻将约战 安卓 明日股市*分析 哈尔滨麻将大全 黑龙江p62走势图带连线 今天深圳风采开奖结果 北京赛车pk10网 新快三无限 双色球走势图下载安装