今天我想出了一个使用Proxy对象的想法,允许创建多个抽象类,例如
const abstract = abstractClass => new Proxy(abstractClass,{
construct: (target, args) => { throw new Error('Error, class was declared as abstract!') }
});
class A{}
class B{}
class C{}
[A,B,C] = [A,B,C].map(c => abstract(c));
所以我的问题是这个解决方案是否有任何缺点,如果有的话,它们是什么。
答案 0 :(得分:3)
缺点是Proxy
无法在ES5中可靠地填充,而且速度很慢。
一个好的方法是用继承来解决这个问题:
class Abstract {
constructor() {
if (Object.getPrototypeOf(this.constructor) === Abstract
|| this.constructor === Abstract) {
throw new Error("Cannot instantiate abstract class");
}
}
};
class AbstractFoo extends Abstract {...}
class ConcreteFoo extends AbstractFoo {...}
Abstract
和AbstractFoo
会在直接实例化时抛出错误。
ES.next装饰器可用于简化任务:
function abstract(target) {
return class Abstract extends target {
constructor(...args) {
super(...args);
if (this.constructor === Abstract) {
throw new Error("Cannot instantiate abstract class");
}
}
};
}
@abstract
class AbstractFoo {...}
class ConcreteFoo extends AbstractFoo {...}
由于装饰器基本上是辅助函数,因此它们可以直接应用于普通的ES6:
const AbstractFoo = abstract(class AbstractFoo {...});
class ConcreteFoo extends AbstractFoo {...}
请注意,对于装饰器,在抛出错误之前将对原始构造函数进行求值,这是使用extends
的代价。解决方法是使用函数而不是类,并手动从target
继承它,类似于TypeScript __extends
和Babel __inherits
辅助函数,这样就可以在super()
之前抛出错误。另一种解决方法是使用new.target
(目前无法通过Babel转换为ES5):
function abstract(target) {
return class Abstract extends target {
constructor(...args) {
if (new.target === Abstract) {
throw new Error("Cannot instantiate abstract class");
}
super(...args);
}
};
}
更简洁的方法是使用TypeScript(在评论中建议),它将抽象类作为语言功能引入,并在设计时处理它。这样就不会污染原型链。