TypeScript 装饰器执行顺序详解:工厂求值与函数执行机制

作者:函数茶馆

时间:2026-10-31

浏览:248

本文深入解析 TypeScript 中装饰器的执行顺序。重点阐述同一声明上多个装饰器的工厂求值(自上而下)与函数执行(自下而上)机制,以及类中实例成员、静态成员、构造函数参数和类装饰器的整体处理流程。通过具体代码示例,清晰展示控制台输出顺序,帮助开发者准确掌握装饰器在类定义时的行为逻辑。

文章目录

TypeScript 中的装饰器遵循一套明确的执行规则,理解这一机制需要从两个维度入手:一是同一个声明上应用多个装饰器的情况,二是类中不同成员被装饰时的整体顺序。

1. 同一声明上多个装饰器的顺序

当某个声明(例如类、方法或属性)被多个装饰器修饰时,其执行过程分为两个阶段:

  • 首先是装饰器表达式(即工厂函数)的求值阶段,这一阶段严格按照代码书写顺序从上到下执行。
  • 其次是装饰器函数(即工厂返回的实际装饰器逻辑)的执行阶段,这一阶段则严格按照代码书写顺序从下到上执行。
function first() {
  console.log('first(): 工厂求值');
  return function (target: any, propertyKey?: string, descriptor?: PropertyDescriptor) {
    console.log('first(): 装饰器执行');
  };
}

function second() {
  console.log('second(): 工厂求值');
  return function (target: any, propertyKey?: string, descriptor?: PropertyDescriptor) {
    console.log('second(): 装饰器执行');
  };
}

class Example {
  @first()
  @second()
  method() {}
}

具体的输出顺序如下所示:

first(): 工厂求值 second(): 工厂求值 second(): 装饰器执行 first(): 装饰器执行

2. 类中不同成员装饰器的整体顺序

对于类内部的不同成员(包括属性、方法、参数等),装饰器的执行顺序取决于成员的类别(实例或静态)以及成员类型,这与源代码中的书写顺序没有直接关系。

整体的求值与执行顺序遵循以下层级:

  1. 实例成员:首先处理参数装饰器,随后处理方法、访问器或属性装饰器。对于每个具体的实例成员,其内部的装饰器按照在类中出现的从上到下顺序进行处理。
  2. 静态成员:处理逻辑与实例成员一致,先执行参数装饰器,再执行方法、访问器或属性装饰器。同样按照在类中出现的从上到下顺序处理每个静态成员。
  3. 接下来处理构造函数参数的装饰器。
  4. 最后执行类装饰器。

可以将上述流程浓缩为一句话:整体顺序为实例成员 → 静态成员 → 构造函数参数 → 类装饰器。在每一组内部,严格遵循成员的源代码顺序;而在每个成员内部,参数装饰器优先于该成员自身的属性、方法或访问器装饰器执行。

3. 示例演示

function classDecorator() {
  console.log('类装饰器工厂');
  return function (constructor: Function) {
    console.log('类装饰器执行');
  };
}

function propertyDecorator(name: string) {
  console.log(`属性装饰器工厂: ${name}`);
  return function (target: any, propertyKey: string) {
    console.log(`属性装饰器执行: ${name}`);
  };
}

function methodDecorator(name: string) {
  console.log(`方法装饰器工厂: ${name}`);
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(`方法装饰器执行: ${name}`);
  };
}

function parameterDecorator(name: string) {
  console.log(`参数装饰器工厂: ${name}`);
  return function (target: any, propertyKey: string, parameterIndex: number) {
    console.log(`参数装饰器执行: ${name}`);
  };
}

@classDecorator()
class Demo {
  @propertyDecorator('实例属性')
  instanceProp: string;

  @propertyDecorator('静态属性')
  static staticProp: string;

  constructor(@parameterDecorator('构造参数') param: string) {
    this.instanceProp = param;
  }

  @methodDecorator('实例方法')
  instanceMethod(@parameterDecorator('实例方法参数') arg: number) {}

  @methodDecorator('静态方法')
  static staticMethod(@parameterDecorator('静态方法参数') arg: number) {}
}

运行以下代码,控制台将按如下顺序输出结果:

属性装饰器工厂: 实例属性 属性装饰器执行: 实例属性 // 实例属性,无参数装饰器 方法装饰器工厂: 实例方法 参数装饰器工厂: 实例方法参数 参数装饰器执行: 实例方法参数 // 实例方法参数优先 方法装饰器执行: 实例方法 // 实例方法在后

属性装饰器工厂: 静态属性 属性装饰器执行: 静态属性 // 静态属性 方法装饰器工厂: 静态方法 参数装饰器工厂: 静态方法参数 参数装饰器执行: 静态方法参数 // 静态方法参数优先 方法装饰器执行: 静态方法 // 静态方法在后

参数装饰器工厂: 构造参数 参数装饰器执行: 构造参数 // 构造函数参数

类装饰器工厂 类装饰器执行 // 类装饰器最后

4. 总结要点

  • 对于同一成员上的多个装饰器:工厂函数自上而下求值,而装饰器函数自下而上执行。
  • 对于类中多个成员的装饰器:先处理实例成员(按从上到下顺序),再处理静态成员(按从上到下顺序),接着处理构造函数参数,最后执行类装饰器。

本文介绍了 TypeScript 装饰器的执行顺序,更多相关内容可参考本站过往文章或继续浏览后续内容,感谢支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章