Typescript 装饰器执行顺序

TypeScript 中装饰器的执行顺序遵循一套固定的规则,可以从同一声明上的多个装饰器类中不同成员的装饰器两个维度来理解。

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

当一个声明(如类、方法、属性)上应用了多个装饰器时:

  • 装饰器表达式 (工厂函数)的求值顺序:从上到下
  • 装饰器函数 (返回的函数本身)的执行顺序:从下到上
typescript 复制代码
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() {}
}

输出顺序:

scss 复制代码
first(): 工厂求值
second(): 工厂求值
second(): 装饰器执行
first(): 装饰器执行

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

对于类中的不同成员(属性、方法、参数等),装饰器的执行顺序由成员类型和所属(实例/静态)决定,与源代码书写顺序无关。

整体求值顺序如下:

  1. 实例成员 :先参数装饰器,再方法/访问器/属性装饰器。
    对于每个实例成员,按照它们在类中出现的从上到下顺序处理。
  2. 静态成员 :同样先参数装饰器,再方法/访问器/属性装饰器。
    对于每个静态成员,按照它们在类中出现的从上到下顺序处理。
  3. 构造函数参数装饰器
  4. 类装饰器(最后执行)。

可以浓缩成一句话:实例 → 静态 → 构造参数 → 类,每一组内部按成员的源代码顺序,且每个成员内是参数装饰器优先于该成员的属性/方法/访问器装饰器。

3. 示例演示

typescript 复制代码
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) {}
}

运行这段代码,控制台输出顺序如下:

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

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

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

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

4. 总结要点

  • 同一成员多装饰器:工厂自上而下求值,装饰器自下而上执行。
  • 多成员:先处理实例成员(从上到下),再处理静态成员(从上到下),然后构造函数参数,最后类装饰器。
相关推荐
一只月月鸟呀1 分钟前
移动端tap与click的区别 && 点透事件
开发语言·前端·javascript
雪隐8 分钟前
AI股票小助手10-我用代码管住自己的手:一个普通人的市场观察笔记
前端·人工智能·后端
KaMeidebaby1 小时前
卡梅德生物技术快报|抗体筛选:抗体筛选中的噬菌体展示四参数调优——从流程设计到数据验证
开发语言·前端·javascript
甲维斯1 小时前
Opus5.0首测,瘫坐沙发!效果,时间,token皆无敌!
前端·人工智能
LVZ1 小时前
Skill + PVM 绝了,AI 直接帮你搞定环境问题了
前端
颜进强1 小时前
从零搭建私人 RAG 知识库:让项目决策真正“可检索、可追溯”
前端·后端·ai编程
颜进强1 小时前
Embedding 模型介绍:热门模型对比与应用场景
前端·后端·ai编程
颜进强1 小时前
LanceDB 基础使用:用 TypeScript 完成第一次向量检索
前端·后端·ai编程
xyphf_和派孔明1 小时前
企业级微前端项目完整创建步骤,第二步:配置主应用
前端
默_笙1 小时前
🔑 让 AI 学会"读"网页:我用 Cheerio 爬了一篇掘金文章,然后切成小块存进了向量库
前端·javascript