一个类会覆盖它所扩展的类的属性:
class ClassA {
msg = 'ClassA'
}
export default class ClassB extends ClassA {
msg = 'ClassB'
onClick() {
console.log(this.msg) // will log "ClassB"
}
}
装饰器会覆盖它所装饰的类的属性:
function ClassA(BaseClass) {
return class extends BaseClass {
msg = 'ClassA'
}
}
@ClassA
export default class ClassB {
msg = 'ClassB'
onClick() {
console.log(this.msg) // will log "ClassA"
}
}
不允许扩展多个类 - 你不能这样做(伪代码):
export default class ClassC extends [ClassB, ClassA] {}
但你可以这样做:
@ClassA
@ClassB
export default class ClassC {}
上面的问题在于“扩展”颠倒了。
decorators
将覆盖 ClassC
已定义的任何属性。但我不想那样。
换句话说,我想使用
decorators
来模拟多个类扩展,但我不希望我的 decorators
覆盖装饰类已定义的任何属性。
我可以执行以下操作:
function ClassA(BaseClass) {
return class extends BaseClass {
constructor() {
super(...arguments)
if (!this.hasOwnProperty('msg')) {
this.msg = 'ClassA'
}
}
}
}
@ClassA
export default class ClassB {
msg = 'ClassB'
onClick() {
console.log(this.msg) // will log "ClassB"
}
}
但这太冗长了。
想象一个具有许多属性的类,其中一些属性甚至是自己装饰的。必须实施下面的上述解决方案似乎是不可持续的:
function ClassA(BaseClass) {
return class extends BaseClass {
msg = 'ClassA' // this should "register" for ClassB, not for ClassC
@service('x') aa // this should "register" for ClassC, not for ClassB
@computed('aa')
get bb() { // this should "register" for ClassC, not for ClassB
return this.aa + 5000
}
}
}
@ClassA
export default class ClassB {
@service('y') aa
@computed('aa')
get bb() {
return this.aa + 3
}
}
@ClassA
export default class ClassC {
msg = 'Class C'
}
有没有一种更简单的方法可以让装饰器不覆盖它正在装饰的类的属性(如果它们已经存在)?
或者我想要实现的目标是不可能的?
注1:我不想使用类扩展,因为我想扩展多个类,这是不可能的,所以我们的想法是使用多个装饰器作为解决方法
注2:请不要建议多个类扩展示例。我已经看过它们了,它们都非常老套,只适用于具有原始属性的非常基本的简单类,而不适用于本身包含修饰属性、复杂方法、其他类实例等的类..
编辑: 我只是想到了这个解决方案,但尚未测试。它的目的是使用装饰器模拟多个类扩展(ClassA、ClassB),同时不允许装饰器覆盖 ClassC 已经定义的属性:
@ClassA
@ClassB
class MyDecorators { /* empty on purpose */ }
export default class ClassC extends MyDecorators {}
您可以创建一个类实例链并通过代理访问它们,并且在陷阱内您可以实现您想要的任何逻辑:
class ClassA {
msg = 'ClassA'
}
class ClassB {
msg = 'ClassB'
logMessage() {
console.log(this.msg) // will log "ClassB"
}
}
class ClassC {
msg = 'ClassC';
}
class ClassD {
logMessage(){
console.log('CLASS D LOGGING MSG:', this.msg);
}
}
function multi(...classChain){ // add support of arguments if needed
return new Proxy(classChain.map(x => new x), {
get(target, prop){
for(const child of target){
if(prop in child){
return child[prop];
}
}
},
set(target, prop, value){
for(child of target){
if(Object.hasOwn(child, prop)){
child[prop] = value
return true;
}
}
}
});
}
const obj = multi(ClassC, ClassB, ClassC);
obj.logMessage();
const obj2 = multi(ClassD, ClassB, ClassC);
obj2.logMessage();