17370845950

javascript的装饰器是什么_如何使用它们增强类与方法的属性【教程】
JavaScript装饰器仍为Stage 3提案,浏览器原生不支持,需Babel或TypeScript编译;仅适用于类及成员,不支持普通函数;方法装饰器接收target、propertyKey、descriptor三参数,在类定义时同步执行。

JavaScript 的装饰器目前仍是 Stage 3 提案,未被正式

纳入标准,所有浏览器原生都不支持,必须通过 Babel(@babel/plugin-proposal-decorators)或 TypeScript 编译才能运行。

装饰器只能用在类和类成员上,不能用于普通函数

这是最容易混淆的一点:你不能写 @log function foo() {} —— 这会直接报语法错误。装饰器的语法糖只作用于类声明、方法、访问器、属性(仅 TS 支持字段装饰器)和参数(实验性,极少用)。

实际可用的场景只有:

  • 类本身:@sealed class MyClass {}
  • 类方法:class C { @readonly method() {} }
  • 类 getter/setter:get @cached value() {}
  • TypeScript 中的字段:class C { @observable name = 'a'; }(需启用 useDefineForClassFields

注意:Babel 默认不支持字段装饰器,TypeScript 需配置 "experimentalDecorators": true"useDefineForClassFields": true 才能解析 @decorator field 语法。

装饰器函数接收什么参数?执行时机在哪?

方法装饰器(最常用)接收三个参数:target(原型对象)、propertyKey(方法名字符串)、descriptor(属性描述符)。它在类定义阶段执行,不是在方法调用时

例如这个日志装饰器:

function log(target, propertyKey, descriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args) {
    console.log(`Calling ${String(propertyKey)} with`, args);
    return original.apply(this, args);
  };
  return descriptor;
}

class Calculator {
  @log
  add(a, b) {
    return a + b;
  }
}

关键点:

  • descriptor.value 是原始方法,必须返回修改后的 descriptor 才生效
  • 不要漏掉 return descriptor,否则方法会被设为 undefined
  • 装饰器函数本身是同步执行的,在模块加载时就完成包装,不是懒加载

Babel 和 TypeScript 的装饰器行为不一致

这是线上出问题的高发区。Babel 的装饰器默认使用「legacy」模式(类似 TS 早期行为),而 TypeScript 5.0+ 默认使用「2025」语义(更贴近提案最终方向),二者对字段初始化、this 绑定、装饰器执行顺序的处理不同。

若你在 Babel 中用了 @init 装饰字段,又在 TS 中编译,大概率会发现值没被正确赋值。解决办法:

  • Babel 配置中显式指定 {"version": "2025"}(需 Babel 7.23+)
  • TypeScript 中保持 "experimentalDecorators": true,但避免混合使用字段装饰器和复杂初始化逻辑
  • 生产环境尽量避免依赖装饰器做关键初始化,改用构造函数或 init() 显式调用

装饰器不是语法糖的终点,而是元编程的入口——但它依赖工具链、版本对齐和明确的执行时序。一旦跨环境部署,@ 符号背后藏着的其实是两套编译器对同一份提案的不同解读。