如何使 ES6 class 最终版(非 subclassible)

How to make ES6 class final (non-subclassible)

假设我们有:

class FinalClass {
  ...
}

如何修改使

class WrongClass extends FinalClass {
  ...
}

new WrongClass(...)

生成异常?也许最明显的解决方案是在 FinalClass 的构造函数中执行以下操作:

if (this.constructor !== FinalClass) {
    throw new Error('Subclassing is not allowed');
}

有没有人有更简洁的解决方案,而不是在每个 class 中重复这些行,这些行应该是最终的(可能使用装饰器)?

FinalClass的构造函数中检查this.constructor,如果不是自己则抛出。 (从@Patrick Roberts 借用 this.constructor 而不是 this.constructor.name 的检查。)

class FinalClass {
  constructor () {
    if (this.constructor !== FinalClass) {
      throw new Error('Subclassing is not allowed')
    }
    console.log('Hooray!')
  }
}

class WrongClass extends FinalClass {}

new FinalClass() //=> Hooray!

new WrongClass() //=> Uncaught Error: Subclassing is not allowed

或者,在支持下,使用 new.target。谢谢@loganfsmyth。

class FinalClass {
  constructor () {
    if (new.target !== FinalClass) {
      throw new Error('Subclassing is not allowed')
    }
    console.log('Hooray!')
  }
}

class WrongClass extends FinalClass {}

new FinalClass() //=> Hooray!

new WrongClass() //=> Uncaught Error: Subclassing is not allowed

______

如您所说,您也可以使用装饰器实现此行为。

function final () {
  return (target) => class {
    constructor () {
      if (this.constructor !== target) {
        throw new Error('Subclassing is not allowed')
      }
    }
  }
}

const Final = final(class A {})()

class B extends Final {}

new B() //=> Uncaught Error: Subclassing is not allowed

正如 Patrick Roberts 在评论中分享的那样,装饰器语法 @final 仍在提案中。它可用于 Babel 和 babel-plugin-transform-decorators-legacy.

constructor.name 很容易被欺骗。只需让子类与超类同名即可:

class FinalClass {
  constructor () {
    if (this.constructor.name !== 'FinalClass') {
      throw new Error('Subclassing is not allowed')
    }
    console.log('Hooray!')
  }
}

const OopsClass = FinalClass

;(function () {
  class FinalClass extends OopsClass {}

  const WrongClass = FinalClass

  new OopsClass //=> Hooray!

  new WrongClass //=> Hooray!
}())

最好检查 constructor 本身:

class FinalClass {
  constructor () {
    if (this.constructor !== FinalClass) {
      throw new Error('Subclassing is not allowed')
    }
    console.log('Hooray!')
  }
}

const OopsClass = FinalClass

;(function () {
  class FinalClass extends OopsClass {}

  const WrongClass = FinalClass

  new OopsClass //=> Hooray!

  new WrongClass //=> Uncaught Error: Subclassing is not allowed
}())