Skip to main content

装饰器

装饰器

装饰者模式:能够在不改变对象自身基础上,在程序运行期间给对象添加职责

装饰器只能针对类和类的属性/方法,不能直接作用于普通函数(由于存在函数声明提升,会导致不可预知的执行顺序)。

注意: 装饰器提案的历史较长,目前主要存在两个版本:

  1. 旧版(Stage 2 / Legacy):早期 TypeScript 和 Babel 实现的版本,广泛应用于 Angular、NestJS 和 MobX 6 之前的版本。其 API 签名通常为 (target, name, descriptor)
  2. 新版(Stage 3 / Standard):目前 ECMAScript 的正式提案版本(Stage 3,22年至今,钉子户实锤了),已在 TypeScript 5.0+ 中原生支持。新版 API 签名发生了变化(接收 valuecontext),且不再依赖 Object.defineProperty

下面的示例主要展示旧版(Legacy)装饰器的实现思想,因为目前大多数成熟的开源框架(如 NestJS)仍在使用此规范。如果你在现代项目中使用,请注意 TypeScript 的 experimentalDecorators 配置。

属性描述符

  • 数据描述符
  • 存取描述符

使用场景(开源库实践):

  • MobX (响应式状态管理):大量使用 @observable 标记响应式状态,@action 标记修改状态的动作,@computed 标记派生属性。
  • NestJS / Angular (依赖注入与路由):使用 @Controller('/users')@Get() 标记路由控制器,使用 @Injectable() 实现控制反转。
  • Core-Decorators (工具库):提供了 @readonly@debounce@time(统计执行时间)等通用装饰器。

类装饰器

log

function log(Class) {
return (...args) => {
console.log(args);
return new Class(...args);
};
}

@log
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
}

const cat = new Animal("Hello kitty", 2);
// ["Hello kitty", 2]
console.log(cat.name);
// Hello kitty

属性/方法装饰器

// 这是一个防抖(Debounce)装饰器的简单实现
function debounce(wait) {
return function (target, name, descriptor) {
const originalMethod = descriptor.value;
let timer = null;

descriptor.value = function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
originalMethod.apply(this, args);
}, wait);
};
return descriptor;
};
}

class SearchInput {
@debounce(500)
handleInput(value) {
console.log(`发送网络请求搜索: ${value}`);
}
}

参考