常见陷阱
- 属性和方法重写问题:
- 当子类重写父类被装饰的方法时,如果装饰器逻辑依赖于特定的属性或状态,可能会出现意外结果。例如,假设父类方法被装饰为记录调用次数,子类重写该方法后,可能会因为装饰器状态的共享或重置问题导致计数不准确。
- 示例代码:
function callCountDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
let callCount = 0;
descriptor.value = function(...args: any[]) {
callCount++;
console.log(`Call count for ${propertyKey}: ${callCount}`);
return originalMethod.apply(this, args);
};
return descriptor;
}
class ParentClass {
@callCountDecorator
someMethod() {
console.log('Parent method');
}
}
class ChildClass extends ParentClass {
@callCountDecorator
someMethod() {
console.log('Child method');
}
}
const parent = new ParentClass();
parent.someMethod(); // 输出:Call count for someMethod: 1,Parent method
const child = new ChildClass();
child.someMethod(); // 预期是独立计数,但这里由于装饰器逻辑,可能和父类计数产生混淆
- 继承链中装饰器的执行顺序:
- 装饰器在继承链中的执行顺序可能不直观。如果装饰器有副作用,比如修改类的原型或添加静态属性,执行顺序可能导致这些副作用在错误的时机发生。
- 示例代码:
function addStaticPropDecorator(target: any) {
target.staticProp = 'Added by decorator';
}
@addStaticPropDecorator
class BaseClass {}
class SubClass extends BaseClass {}
console.log(SubClass.staticProp); // 这里期望能访问到静态属性,但由于装饰器执行顺序,可能在子类实例化时还未正确添加
- 装饰器状态共享问题:
- 如果装饰器使用了闭包内的状态(如上述的
callCount
),在继承结构中不同实例间可能会共享这个状态,导致不符合预期的行为。
优化方案
- 针对属性和方法重写问题:
- 可以让装饰器在子类重写方法时,保持独立的状态。例如,通过在实例上存储状态而不是在闭包内共享状态。
- 改进后的代码:
function callCountDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
if (!this[`${propertyKey}_callCount`]) {
this[`${propertyKey}_callCount`] = 0;
}
this[`${propertyKey}_callCount`]++;
console.log(`Call count for ${propertyKey}: ${this[`${propertyKey}_callCount`]}`);
return originalMethod.apply(this, args);
};
return descriptor;
}
class ParentClass {
@callCountDecorator
someMethod() {
console.log('Parent method');
}
}
class ChildClass extends ParentClass {
@callCountDecorator
someMethod() {
console.log('Child method');
}
}
const parent = new ParentClass();
parent.someMethod(); // 输出:Call count for someMethod: 1,Parent method
const child = new ChildClass();
child.someMethod(); // 输出:Call count for someMethod: 1,Child method,计数独立
- 针对继承链中装饰器的执行顺序:
- 确保装饰器的副作用在合适的时机执行。可以通过在装饰器内部检查是否为子类,并根据情况调整逻辑。
- 改进后的代码:
function addStaticPropDecorator(target: any) {
if (!target.prototype.isSubclass) {
target.staticProp = 'Added by decorator';
}
target.prototype.isSubclass = true;
}
@addStaticPropDecorator
class BaseClass {}
class SubClass extends BaseClass {}
console.log(SubClass.staticProp); // 现在可以正确访问静态属性
- 针对装饰器状态共享问题:
- 采用实例级别的状态管理,如上述解决属性和方法重写问题中通过实例属性存储状态的方式,避免状态在不同实例间共享。