TypeScript装饰器调用时机存疑:究竟何时执行?
哈哈,这个困惑太常见了!我当初刚接触TypeScript装饰器的时候也踩过这个坑——不同类型的装饰器执行时机真的不一样,很容易搞混。下面就把这个问题掰扯明白:
TypeScript装饰器的执行时机:一次说清楚
先给你一个核心结论:TypeScript装饰器的执行时机完全取决于它装饰的目标(类、属性、方法、参数等),根本不是统一在构造函数后或者实例化时!
1. 类装饰器:类声明时就执行,和实例化半毛钱关系没有
这就是你看到的高赞回答说的情况。类装饰器是在代码执行到类声明那一行的时候就直接跑了,不管你后续有没有new这个类。举个直观的例子:
function ClassDecorator(target: Function) { console.log("类装饰器执行了"); } @ClassDecorator class MyClass { constructor() { console.log("构造函数执行了"); } } // 注意!此时控制台已经输出"类装饰器执行了",哪怕我们还没实例化任何对象 const instance = new MyClass(); // 这时候才会输出"构造函数执行了"
你看,类装饰器的执行早于任何实例的构造函数调用,甚至不需要创建实例就会触发。
2. 属性/方法/参数装饰器:实例化时,在构造函数执行前、属性初始化前运行
这就是Angular讲师提到的场景。当你给类的属性、方法或者参数加装饰器时,这些装饰器会在每次实例化类的时候,先于构造函数执行,而且是在属性被赋值之前。比如:
function PropertyDecorator(target: any, propertyKey: string) { console.log(`属性装饰器执行了:${propertyKey}`); // 这里打印target[propertyKey]会得到undefined,因为属性还没初始化 } function MethodDecorator(target: any, methodName: string, descriptor: PropertyDescriptor) { console.log(`方法装饰器执行了:${methodName}`); } class MyClass { @PropertyDecorator myProperty = "初始化值"; constructor() { console.log("构造函数执行了"); } @MethodDecorator myMethod() {} } const instance = new MyClass(); // 控制台输出顺序: // 属性装饰器执行了:myProperty // 方法装饰器执行了:myMethod // 构造函数执行了
这里能明显看到,属性和方法装饰器都在构造函数之前运行,而且此时类的属性还没被赋初始值。
为什么你的实验结果会相悖?
大概率是你混淆了不同装饰器的类型,或者踩了「装饰器工厂函数」的坑。举个常见误区:如果你的装饰器是工厂函数(也就是返回一个装饰器函数的函数),那工厂函数的执行时机是类声明时,但它返回的实际装饰器函数的执行时机才对应目标的时机。比如:
function DecoratorFactory() { console.log("工厂函数执行了(类声明时)"); return function(target: any) { console.log("实际装饰器执行了"); } } @DecoratorFactory() class MyClass {} // 输出顺序: // 工厂函数执行了(类声明时) // 实际装饰器执行了(类装饰器,同样是类声明时)
如果你把工厂函数的执行当成了装饰器的执行,可能就会误以为时机不对。另外,如果是方法装饰器里做了函数包装(比如修改descriptor.value),那包装后的函数是在调用方法时才执行,这和装饰器本身的执行时机是两回事哦!
最后总结不同装饰器的执行时机
- 类装饰器:类声明时执行,仅一次
- 属性/方法/参数装饰器:每次实例化类时,在构造函数执行前、属性初始化前执行
- 装饰器工厂函数:类声明时执行,返回的装饰器函数按对应目标时机执行
希望这个能帮你理清困惑!
内容的提问来源于stack exchange,提问作者Eddie Lin
相关产品推荐
相关产品推荐

