JavaScript装饰器处于Stage 3,非标准语法,仅适用于类及成员,依赖Object.defineProperty和Reflect API,执行于类定义阶段,需Babel/TS配置对齐,配合reflect-metadata才支持运行时元数据。

装饰器只能用在类和类成员上,不支持普通函数或变量
你不能写 @log function foo() {},也不能给 const x = @memoize 42 加装饰器。合法位置只有:
- 类声明(
@sealed class Foo {}) - 类方法(
class Foo { @debounce(300) handleClick() {} }) - getter/setter(
@readonly get value() {}) - 类字段(
class Foo { @observable count = 0 },需启用decoratorAutoAccessors或使用提案早期版本)
accessor 字段(即带 accessor 关键字的字段)的支持,且需显式开启实验性配置。
装饰器函数接收三个参数:target、name、descriptor
以方法装饰器为例,它的签名是: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 和 TypeScript 对装饰器的支持差异很大
两者都支持装饰器,但语义不一致:
- Babel(
@babel/plugin-proposal-decorators)默认使用legacy: true模式,模拟 TypeScript 旧版行为;设为legacy: false才对接 Stage 3 提案(需同时启用decoratorAutoAccessors) - TypeScript 在
experimentalDecorators: true下实现的是自定义的、非标准的装饰器模型,其类装饰器传入的是构造函数而非原型,与 Stage 3 不兼容 - Vite / Next.js 等工具链若未显式配置 Babel 插件,
@语法会直接报错Unexpected token '@'
装饰器本身不提供运行时反射能力,需配合 Metadata API
装饰器能“打标记”,但无法自动读取这些标记——除非你手动存到 target 上,或使用 reflect-metadata:
-
Reflect.defineMetadata('role', 'admin', target, name)可存储元数据 -
Reflect.getMetadata('role', target, name)可在运行时读取 - 但该 API 未被标准化,仅作为 polyfill 存在,Node.js 18+ 需手动
import 'reflect-metadata'
this 尚未绑定,也无法访问实例状态。想做实例级逻辑(比如基于 this.id 动态生成缓存 key),必须把逻辑延迟到方法内部。











