JavaScript装饰器仍为Stage 3提案,浏览器原生不支持,需Babel或TypeScript编译;仅适用于类及成员,不支持普通函数;方法装饰器接收target、propertyKey、descriptor三参数,在类定义时同步执行。
JavaScript 的装饰器目前仍是 Stage 3 提案,未被正式

@babel/plugin-proposal-decorators)或 TypeScript 编译才能运行。
这是最容易混淆的一点:你不能写 @log function foo() {} —— 这会直接报语法错误。装饰器的语法糖只作用于类声明、方法、访问器、属性(仅 TS 支持字段装饰器)和参数(实验性,极少用)。
实际可用的场景只有:
@sealed class MyClass {}
class C { @readonly method() {} }
get @cached value() {}
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 的装饰器默认使用「legacy」模式(类似 TS 早期行为),而 TypeScript 5.0+ 默认使用「2025」语义(更贴近提案最终方向),二者对字段初始化、this 绑定、装饰器执行顺序的处理不同。
若你在 Babel 中用了 @init 装饰字段,又在 TS 中编译,大概率会发现值没被正确赋值。解决办法:
{"version": "2025"}(需 Babel 7.23+)"experimentalDecorators": true,但避免混合使用字段装饰器和复杂初始化逻辑init() 显式调用装饰器不是语法糖的终点,而是元编程的入口——但它依赖工具链、版本对齐和明确的执行时序。一旦跨环境部署,@ 符号背后藏着的其实是两套编译器对同一份提案的不同解读。