最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to make ES6 class final (non-subclassible) - Stack Overflow

programmeradmin2浏览0评论

Assume we have:

class FinalClass {
  ...
}

How to modify it to make

class WrongClass extends FinalClass {
  ...
}

or

new WrongClass(...)

to generate an exception? Perhaps the most obvious solution is to do the following in the FinalClass's constructor:

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

Does anyone have a more cleaner solution instead of repeating these lines in each class that supposed to be final (probably with a decorator)?

Assume we have:

class FinalClass {
  ...
}

How to modify it to make

class WrongClass extends FinalClass {
  ...
}

or

new WrongClass(...)

to generate an exception? Perhaps the most obvious solution is to do the following in the FinalClass's constructor:

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

Does anyone have a more cleaner solution instead of repeating these lines in each class that supposed to be final (probably with a decorator)?

Share Improve this question edited Oct 23, 2016 at 23:52 Felix Kling 816k180 gold badges1.1k silver badges1.2k bronze badges asked Aug 3, 2016 at 15:50 Dmitry DruganovDmitry Druganov 2,3583 gold badges24 silver badges33 bronze badges 7
  • 3 Interesting question. Is there a reason you're trying to do this? – gcampbell Commented Aug 3, 2016 at 15:52
  • Since ultimately this is all compiled down to Javascript (< 5), where such things don't exist and everything is just an object which is infinitely malleable at runtime, I think chances for this are bad. – deceze Commented Aug 3, 2016 at 15:54
  • To more strongly phrase the point implied by @gcampbell, this strikes me as a bad idea, bordering on terrible. For example, strings in Java suck in no small part because the class lacks many useful methods and was finaled as a premature performance hack. – Jared Smith Commented Aug 3, 2016 at 16:07
  • @deceze if we're talking ES 5+ then Object.freeze can be used, Object.defineProperty, etc., recursively even if need be (to 'deep freeze'). I just don't know why you would, in general, want to do that to a class other than performance and it would have to be one hell of a perf improvement. – Jared Smith Commented Aug 3, 2016 at 16:09
  • 1 Your argument applies to class based languages. Making a "class" final doesn't make much sense in JavaScript, as there are many ways to extend and create an object. – a better oliver Commented Aug 8, 2016 at 12:50
 |  Show 2 more comments

3 Answers 3

Reset to default 15

Inspect this.constructor in the constructor of FinalClass and throw if it is not itself. (Borrowing inspection of the this.constructor instead of this.constructor.name from @Patrick Roberts.)

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

Alternatively, with support, use new.target. Thanks @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

______

As you say, you could also achieve this behaviour with a decorator.

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

As Patrick Roberts shared in the comments the decorator syntax @final is still in proposal. It is available with Babel and babel-plugin-transform-decorators-legacy.

constructor.name is easy enough to spoof. Just make the subclass the same name as the superclass:

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!
}())

Better to check the constructor itself:

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
}())

Assuming you are using TypeScript, the simpliest way of achieving this is to set the constructor to private, as in:

        class MyClass {

            private constructor() {}

        }

Then, you should get a compile-time error if you try to instantiate, or extend, this class.

发布评论

评论列表(0)

  1. 暂无评论