装饰器执行顺序

理解装饰器的执行顺序对于正确使用装饰器至关重要。不同类型的装饰器有不同的执行顺序,多个装饰器的组合也有特定的执行规则。

装饰器工厂执行顺序

当使用装饰器工厂时,工厂函数按从上到下的顺序执行,而装饰器本身按从下到上的顺序执行。

function first() {
  console.log("first(): 工厂")
  return function (target: any) {
    console.log("first(): 装饰器")
  }
}

function second() {
  console.log("second(): 工厂")
  return function (target: any) {
    console.log("second(): 装饰器")
  }
}

@first()
@second()
class MyClass {}

输出:

first(): 工厂
second(): 工厂
second(): 装饰器
first(): 装饰器

类成员装饰器执行顺序

类成员装饰器按以下顺序执行:

  1. 参数装饰器
  2. 方法装饰器 / 访问器装饰器 / 属性装饰器
function methodDecorator() {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("方法装饰器")
  }
}

function propertyDecorator() {
  return function (target: any, propertyKey: string) {
    console.log("属性装饰器")
  }
}

function paramDecorator() {
  return function (target: any, propertyKey: string, parameterIndex: number) {
    console.log("参数装饰器")
  }
}

class Example {
  @propertyDecorator()
  name: string = ""

  @methodDecorator()
  greet(@paramDecorator() name: string): string {
    return `你好,${name}`
  }
}

输出:

参数装饰器
方法装饰器
属性装饰器

完整执行顺序

完整的装饰器执行顺序:

  1. 实例成员的参数装饰器
  2. 实例成员的方法/访问器/属性装饰器
  3. 静态成员的参数装饰器
  4. 静态成员的方法/访问器/属性装饰器
  5. 构造函数的参数装饰器
  6. 类装饰器
function classDecorator() {
  return function (target: any) {
    console.log("类装饰器")
  }
}

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

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

function paramDecorator(name: string) {
  return function (target: any, propertyKey: string, parameterIndex: number) {
    console.log(`参数装饰器: ${name}`)
  }
}

@classDecorator()
class Example {
  @propertyDecorator("实例属性")
  instanceProp: string = ""

  @methodDecorator("实例方法")
  instanceMethod(@paramDecorator("实例参数") param: string) {}

  @propertyDecorator("静态属性")
  static staticProp: string = ""

  @methodDecorator("静态方法")
  static staticMethod(@paramDecorator("静态参数") param: string) {}

  constructor(@paramDecorator("构造函数参数") param: string) {}
}

输出:

参数装饰器: 实例参数
方法装饰器: 实例方法
属性装饰器: 实例属性
参数装饰器: 静态参数
方法装饰器: 静态方法
属性装饰器: 静态属性
参数装饰器: 构造函数参数
类装饰器

同一成员多个装饰器

同一成员上的多个装饰器,工厂函数从上到下执行,装饰器从下到上执行。

function decorator(name: string) {
  console.log(`${name}: 工厂`)
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(`${name}: 装饰器`)
  }
}

class Example {
  @decorator("A")
  @decorator("B")
  @decorator("C")
  method() {}
}

输出:

A: 工厂
B: 工厂
C: 工厂
C: 装饰器
B: 装饰器
A: 装饰器

方法装饰器链

方法装饰器可以形成调用链,外层装饰器先执行。

function log(prefix: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value
    descriptor.value = function (...args: any[]) {
      console.log(`${prefix}: 开始`)
      const result = original.apply(this, args)
      console.log(`${prefix}: 结束`)
      return result
    }
    return descriptor
  }
}

class Example {
  @log("A")
  @log("B")
  method() {
    console.log("执行方法")
  }
}

const example = new Example()
example.method()

输出:

A: 开始
B: 开始
执行方法
B: 结束
A: 结束

实际应用

理解执行顺序对于实现复杂功能很重要。

function validate() {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("验证装饰器")
    return descriptor
  }
}

function cache() {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("缓存装饰器")
    return descriptor
  }
}

function log() {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("日志装饰器")
    return descriptor
  }
}

class DataService {
  @log()
  @cache()
  @validate()
  fetchData(id: number) {
    console.log("获取数据")
    return { id, data: "数据" }
  }
}

const service = new DataService()
service.fetchData(1)

执行顺序:验证 -> 缓存 -> 日志

小结

装饰器工厂函数从上到下执行,装饰器本身从下到上执行。类成员装饰器按参数装饰器、方法/属性装饰器的顺序执行。实例成员装饰器先于静态成员装饰器执行。构造函数参数装饰器在类装饰器之前执行。理解装饰器执行顺序对于正确组合装饰器至关重要。