Setter/Getter没有在属性的第二个装饰器上运行



我有2个装饰器,当我将它们附加到一个属性时,只有第一个定义的执行它的setter/getter属性。内部函数本身运行Init Decorator 1Init Decorator 2。是什么原因导致第二个装饰器不执行setter/getter?

我是这样定义我的两个装饰器的:

export function Decorator1(): any {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
descriptor = descriptor || {};
console.log('Init Decorator 1');
descriptor.get = function (this: any) { console.log('Get Decorator 1'); }
descriptor.set = function (this: any) { console.log('Set Decorator 1'); }
return descriptor;
}
}
export function Decorator2(): any {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
descriptor = descriptor || {};
console.log('Init Decorator 2');
descriptor.get = function (this: any) { console.log('Get Decorator 2'); }
descriptor.set = function (this: any) { console.log('Set Decorator 2'); }
return descriptor;
}
}

然后我使用装饰符如下:

export class Test {
@Decorator1()
@Decorator2()
code = '';
constructor() {
setTimeout(() => this.code = '123', 2000);
}
}
new Test();

操场例子

[LOG]: "Init Decorator 2" 
[LOG]: "Init Decorator 1" 
[LOG]: "Set Decorator 1" 
[LOG]: "Set Decorator 1" 

根据TypeScript访问器装饰器的文档,当这样的装饰器返回一个值时,它会替换相关属性的属性描述符。当你编写装饰器时,它们是自下而上运行的。

因此,在您的示例代码 中
@Decorator1()
@Decorator2()
code = '';

您首先使用最初未定义的属性描述符并将其替换为Decorator2描述符。然后,您将使用Decorator2描述符,并通过重新分配其getset属性来修改它。重新分配属性不会以任何方式保留先前的属性。包含"Set Decorator 2"set()方法已被丢弃。哎呀。


如何组合属性描述符取决于您。假设您应该分析传递给decorator的descriptor值,并保留其getset属性。例如:

return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
descriptor = descriptor || {};
const prevGet = descriptor.get;
const prevSet = descriptor.set;
console.log('Init Decorator N');
descriptor.get = function (this: any) {
console.log('Get Decorator N');
if (prevGet) prevGet.call(this);
}
descriptor.set = function (this: any, v) {
console.log('Set Decorator N');
if (prevSet) prevSet.call(this, v);
}
return descriptor;
}

这里我们保留了getset之前的值,如果它们已经定义,则在新的getset方法中调用它们。请注意,我并不担心前面的方法返回什么,特别是对于get,您可能会这样做。按什么顺序运行取决于你;在这里,我在现有方法逻辑之前运行新方法逻辑。这里重要的一点是,如果您想以某种方式保留先验描述符状态,则需要显式地关心它。

使用这种方法会得到以下结果:

[LOG]: "Init Decorator 2" // Decorator2 runs first
[LOG]: "Init Decorator 1" // Then Decorator1, 
[LOG]: "Set Decorator 1" // Decorator1's logic runs first 
[LOG]: "Set Decorator 2" // Then Decorator2's logic is run inside of Decorator1's set
[LOG]: "Set Decorator 1" // ditto
[LOG]: "Set Decorator 2" // ditto

Playground链接到代码

相关内容