JavaScript装饰器处于Stage 3,非标准语法,仅适用于类及成员,依赖Object.defineProperty和Reflect API,执行于类定义阶段,需Babel/TS配置对齐,配合reflect-metadata才支持运行时元数据。JavaScript 的装饰器提案(Decorator Proposal)目前处于 Stage 3(TC39),**不是已标准化的语法,不能直接在生产环境无编译地使用**。它本质上是一套用于**声明式增强类、方法、访问器或属性**的元编程语法糖,底层依赖于 `Object.defineProperty` 和 `Reflect` API 实现行为注入。
你不能写 @log function foo() {},也不能给 const x = @memoize 42 加装饰器。合法位置只有:
@sealed class Foo {})class Foo { @debounce(300) handleClick() {} })@readonly get value() {})class Foo { @observable count = 0 },需启用 decoratorAutoAccessors 或使用提案早期版本)accessor 字段(即带 accessor 关键字的字段)的支持,且需显式开启实验性配置。
以方法装饰器为例,它的签名是:function decorator(target, name, descriptor),其中:
target 是类的原型(方法装饰器)或构造函数(类装饰器)name 是方法名(string)descriptor 是属性描述符对象,含 value(原始方法)、get/set、configurable、enumerable 等descriptor.value 来包装原方法,或替换为新的 getter/setter。例如实现一个简单的日志装饰器:
function log(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function(...args) {
console.log(`Calling ${name} with`, args);
return original.apply(this, args);
};
}
两者都支持装饰器,但语义不一致:
@babel/plugin-proposal-decorators)默认使用 legacy: true 模式,模拟 TypeScript 旧版行为;设为 legacy: false 才对接 Stage 3 提案(需同时启用 decoratorAutoAccessors)experimentalDecorators: true 下实现的是自定义的、非标准的装饰器模型,其类装饰器传入的是构造函数而非原型,与 Stage 3 不兼容@ 语法会直接报错 Unexpected token '@'
装饰器能“打标记”,但无法自动读取这些标记——除非你手动存到 target 上,或使用 reflect-metadata:
Reflect.defineMetadata('role', 'admin', target, name) 可存储元数据Reflect.getMetadata('role', target, name) 可在运行时读取import 'reflect-metadata'
用时**。所有装饰器函数会在模块加载时同步执行一次,此时 this 尚未绑定,也无法访问实例状态。想做实例级逻辑(比如基于 this.id 动态生成缓存 key),必须把逻辑延迟到方法内部。