一、背景

随着 TypeScript 和 ES6 里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。

注:装饰器是一项实验性特性,在未来的版本中可能会发生改变。 若要启用实验性的装饰器特性,你必须在命令行或 tsconfig.json 里启用 experimentalDecorators 编译器选项。

  1. {
  2. "compilerOptions": {
  3. "target": "ES5",
  4. "experimentalDecorators": true
  5. }
  6. }

二、简介

1 概念

装饰器是一种特殊类型的声明,它能够被附加到类声明方法访问符属性参数上。 装饰器使用表达式 @expression 这种形式,expression 求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

2 定义

2.1 普通函数

装饰器要求值必须是一个函数,因此一个普通的函数也可以作为装饰器的值:

  1. function aheadOfSomething() {
  2. }

2.2 装饰器工厂

可以将装饰器的表达式写为一个函数调用,表达式执行结果为另一个函数即可,被调用的函数称为装饰器工厂:

  1. // 装饰器工厂
  2. function decoratorFactory(value) {
  3. // 返回的装饰器
  4. return function () {
  5. }
  6. }

3 使用

装饰器的使用方法即在要附加的类声明、方法、访问符、属性或参数前使用 @expression 的形式即可,可以对一个声明前使用多个装饰器

  1. // 单个装饰器
  2. @aheadOfSomething
  3. // 多个装饰器
  4. @aheadOfOne
  5. @aheadOfTwo
  6. // 单个装饰器工厂
  7. @decoratorFactory()
  8. // 多个装饰器工厂
  9. @aheadOfOne(1)
  10. @aheadOfTwo(2)

❗注意:多个装饰器应用在一个声明上时:

  • 装饰器表达式求值按代码书写顺序从上往下求值;
  • 访问声明前按从下往上执行装饰器函数;

三、分类

1 类装饰器

类装饰器应用于类的构造函数,可以用来监视修改替换类的定义。类装饰器表达式会在运行时当作函数被调用,参数是类的构造函数

如果类装饰器返回一个值,它会使用返回的新构造函数来替换类的声明。但必须手动处理好原来逻辑中的原型链。

  1. // 声明类装饰器
  2. function ObjectSeal(constructor: Function) {
  3. /**
  4. * Object.seal 即封闭一个对象,
  5. * 使其不能更改现有属性配置
  6. * 更不能添加或删除属性
  7. * 但现有可写的值,仍然可写
  8. * 类似于 Object.freeze (值都不能更改)
  9. */
  10. Object.seal(constructor);
  11. Object.seal(constructor.prototype);
  12. console.log(`ObjectSeal is called.`);
  13. }
  14. // 使用类装饰器
  15. @ObjectSeal
  16. class User {
  17. name: string;
  18. constructor(name: string) {
  19. this.name = name;
  20. console.log(`User constructor is called.`);
  21. }
  22. sayHi() {
  23. console.log(`Hi! I'm ${this.name}.`);
  24. }
  25. }
  26. // 调用类构造函数前会执行类装饰器
  27. const xiaoqiang = new User("小强");
  28. console.log(xiaoqiang, Object.isSealed(User), Object.isSealed(xiaoqiang));

执行结果

  1. ObjectSeal is called.
  2. User constructor is called.
  3. User { name: '小强' } true false

2 方法装饰器

typescript-logo.jpg