利用 es6 new.target 來對模擬抽象類

起源

最近在使用 Symbol 來作爲惟一值,發現 Symbol 沒法進行 new 操做,只能看成函數使用,只要進行了new 就會發生類型錯誤java

new Symbol()

// error
Uncaught TypeError: Symbol is not a constructor
    at new Symbol (<anonymous>)
    at <anonymous>:1:1
複製代碼

在不考慮底層實現的狀況下,在代碼層面是否可以實現一個函數只能夠進行調用而不能夠進行 new 操做呢?思考以後以下寫出:node

function disConstructor() {
  if (this !== window) {
    throw new TypeError(' disConstructor is not a constructor')
  }
  console.log('gogo go')
}

// 測試結果以下
disConstructor() // gogo go

new disConstructor()

// error
Uncaught TypeError:  disConstructor is not a constructor
    at new disConstructor (<anonymous>:3:15)
    at <anonymous>:1:1
複製代碼

若是使用 nodejs,window 能夠切換爲 global, 代碼運行結果不變,由於對於我的而言沒有適用場景。因而就沒有繼續研究下去,但是最近在重新翻閱 es6 時候發現 new.target這個屬性。es6

new.target 屬性

介紹(引用 mdn 文檔)

new.target屬性容許你檢測函數或構造方法是不是經過new運算符被調用的。
在經過new運算符被初始化的函數或構造方法中,new.target返回一個指向構造方法或函數的引用。在普通的函數調用中,new.target 的值是undefined。bash

這樣的話 咱們的代碼就能夠這樣改成編輯器

function disConstructor() {
  // 普通的函數調用中,new.target 的值是undefined。
  if (new.target) {
    throw new TypeError(' disConstructor is not a constructor')
  }
  console.log('gogo go')
}
複製代碼

獲得與上述代碼同樣的答案。函數

深刻

難道 es6 特意添加的功能僅僅只能用於檢查一下咱們的函數調用方式嗎?
在查閱的過程各類發現了大多數都方案都是用 new.target 寫出只能被繼承的類。相似於實現java的抽象類。測試

class Animal {
  constructor(name, age) {
    if (new.target === Animal) {
      throw new Error('Animal class can`t instantiate');
    }
    this.name = name
    this.age = age
  }
  // 其餘代碼
  ...
}

class Dog extends Animal{
  constructor(name, age, sex) {
    super(name, age)
    this.sex = sex
  }
}

new Animal()
// error
Uncaught Error: Animal class can`t instantiate
    at new Animal (<anonymous>:4:13)
    at <anonymous>:1:1

new Dog('mimi', 12, '公')
// Dog {name: "mimi", age: 12, sex: "公"}

複製代碼

可是 java抽象類抽象方法須要重寫,這個是沒有方案的。因而在測試與使用的過程當中,卻意外發現了超類能夠在構造期間訪問派生類的原型,利用起來。this

class Animal {
  constructor(name, age) {
    console.log(new.target.prototype)
  }
  // 其餘代碼
  ...
}
複製代碼

以前運行時調用須要重寫的方法報錯是這樣寫的。spa

class Animal {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  getName () {
    throw new Error('please overwrite getName method')
  }
}

class Dog extends Animal{
  constructor(name, age, sex) {
    super(name, age)
    this.sex = sex
  }
}

const pite = new Dog('pite', 2, '公')
a.getName()
// error
Uncaught Error: please overwrite getName method
    at Dog.getName (<anonymous>:8:11)
    at <anonymous>:1:3
複製代碼

然而此時利用 new.target ,我就能夠利用 構造期間 對子類進行操做報錯。prototype

class Animal {
  constructor(name, age) {
    // 若是 target 不是 基類 且 沒有 getName 報錯
    if (new.target !== Animal && !new.target.prototype.hasOwnProperty('getName')) {
      throw new Error('please overwrite getName method')
    }
    this.name = name
    this.age = age
  }
}

class Dog extends Animal{
  constructor(name, age, sex) {
    super(name, age)
    this.sex = sex
  }
}

const pite = new Dog('pite', 2, '公')
// error
Uncaught Error: please overwrite getName method
    at new Animal (<anonymous>:5:13)
    at new Dog (<anonymous>:14:5)
    at <anonymous>:1:1
複製代碼

此時能夠把運行方法時候發生的錯誤提早到構造時期,雖然都是在運行期,可是該錯誤觸發機制要早危害要大。反而對代碼是一種保護。

固然了,利用超類能夠在構造期間訪問派生類的原型做用遠遠不是那麼簡單,必然是很強大的,能夠結合業務場景談一談理解和做用。

其餘方案

增長 編輯器插件 proxy 修飾器

相關文章
相關標籤/搜索