我有2个装饰器,当我将它们附加到一个属性时,只有第一个定义的执行它的setter/getter
属性。内部函数本身运行Init Decorator 1
和Init 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
描述符,并通过重新分配其get
和set
属性来修改它。重新分配属性不会以任何方式保留先前的属性。包含"Set Decorator 2"
的set()
方法已被丢弃。哎呀。
如何组合属性描述符取决于您。假设您应该分析传递给decorator的descriptor
值,并保留其get
和set
属性。例如:
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;
}
这里我们保留了get
和set
之前的值,如果它们已经定义,则在新的get
和set
方法中调用它们。请注意,我并不担心前面的方法返回什么,特别是对于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链接到代码