TypeScript多装饰器修饰属性时仅外层setter/getter执行问题
问题原因
内层装饰器setter/getter未触发的核心原因有两点:
- 多层装饰器叠加执行时,外层装饰器直接覆盖了内层装饰器定义在
PropertyDescriptor上的存取器方法,没有保留内层方法的引用,最终挂载到属性上的只有最外层装饰器的get/set逻辑。 - 装饰器执行顺序符合如下规则:
- 装饰器工厂函数(即
Decorator1()、Decorator2()本身)从上到下求值 - 求值得到的实际装饰器逻辑从下到上(从内到外)调用
对应代码逻辑,会先执行内层Decorator2给descriptor写入存取器,再执行外层Decorator1,直接重写descriptor的get/set字段,内层写入的逻辑被完全覆盖,因此运行时不会触发。
另外原有存取器实现仅打印日志,没有维护实际属性值、也没有调用上层逻辑,即使不被覆盖也无法实现属性的正常读写。
- 装饰器工厂函数(即
修复方案
每层装饰器重写get/set前,先保存当前descriptor上已有的存取器引用,自身逻辑执行完成后链式调用上一层的存取器,同时维护属性的实际存储值即可,修复后的装饰器代码如下:
export function Decorator1(): any { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { descriptor = descriptor || {}; console.log('Init Decorator 1'); // 保存内层装饰器已定义的存取器 const originalGet = descriptor.get; const originalSet = descriptor.set; let storedValue: any; descriptor.get = function (this: any) { console.log('Get Decorator 1'); // 优先调用内层get逻辑 if (originalGet) return originalGet.call(this); return storedValue; } descriptor.set = function (this: any, newValue: any) { console.log('Set Decorator 1'); // 优先调用内层set逻辑 if (originalSet) { originalSet.call(this, newValue); return; } storedValue = newValue; } return descriptor; } } export function Decorator2(): any { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { descriptor = descriptor || {}; console.log('Init Decorator 2'); const originalGet = descriptor.get; const originalSet = descriptor.set; let storedValue: any; descriptor.get = function (this: any) { console.log('Get Decorator 2'); if (originalGet) return originalGet.call(this); return storedValue; } descriptor.set = function (this: any, newValue: any) { console.log('Set Decorator 2'); if (originalSet) { originalSet.call(this, newValue); return; } storedValue = newValue; } return descriptor; } }
修复后运行结果
执行代码后日志输出如下,两层装饰器的存取器逻辑都会按从外到内的顺序触发:
[LOG]: "Init Decorator 2" [LOG]: "Init Decorator 1" [LOG]: "Set Decorator 1" [LOG]: "Set Decorator 2" [LOG]: "Set Decorator 1" [LOG]: "Set Decorator 2"
内容的提问来源于stack exchange,提问作者Get Off My Lawn
相关产品推荐
相关产品推荐

