TypeScript 实现装饰者模式

463 阅读2分钟

这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战

装饰者模式

下面我们来学习装饰者模式,首先什么是装饰者模式呢?

装饰者模式就是在不侵入原来的类的情况下,改变或者拓展它。

我们来看这个例子:

// Phone.ts
export default class Phone {
  getPrice() {
    return 1000;
  };
  call() {
    console.log('calling');
  }
}

接下来,我们想给这个手机添加手机壳,我们可以添加一个装饰者。

一个装饰者其实就是普通的 function。

// addCase.ts
import Phone from './Phone';

// 添加手机壳
export default function addCase(P: typeof Phone) {
  return class extends P {
    getPrice() {
      return super.getPrice() + 50;
    }
  }
}

那我们如何使用这个装饰者呢?首先我们还是要引入装饰器,修改下面这样,这样就实现了给这个手机添加手机壳

// Phone.ts

import addCase from './addCase';

class Phone {
  getPrice() {
    return 1000;
  };
  call() {
    console.log('calling');
  }
}

export default addCase(Phone);

我们在使用它的时候,getPrice 就可以返回 1050。

// index.ts
import Phone from './Phone';

let p = new Phone();

console.log(p.getPrice()); // 1050

除了这种直接调用装饰器的方式,我们还可以使用更加简便的方式,在 class 前面使用 @ 操作符,然后将 class 标为默认导出。这样也能实现同样的效果。

// Phone.ts

import addCase from './addCase';

@addCase
export default class Phone {
  getPrice() {
    return 1000;
  };
  call() {
    console.log('calling');
  }
}

习题:理解装饰者模式,计算下面结果

image.png

答案:

20

解析:

装饰者模式在不改变对象本身的情况下,在程序运行阶段为对象动态增加功能。

ES6 开始用 @ 标识装饰器,即 @decorator,题目中的类 animalDecorator 是一个类装饰器,类装饰器会在运行时当作函数被调用,类的构造函数将作为其唯一的参数。

装饰器中使用了泛型相关知识 <>(我们将在泛型章节详细介绍)。

为了更好的理解装饰器,我们看看 tsc 编译后的核心代码

const animalDecorator = (constructor) => {
    return class extends constructor {
        constructor() {
            super(...arguments);
            this.age = 20;
        }
    };
};
let Animal = class Animal {
    constructor(age) {
        this.age = age;
    }
};
Animal = __decorate([
    animalDecorator
], Animal);
new Animal(10).age;

类装饰器 animalDecorator 返回了一个匿名类,记做匿名类 A 下面简称类 A 。类 A 继承于类 Animal 的构造函数。运行时候会先执行类 Animal 的构造函数,在执行类 A 的构造函数,这样也就达到了装饰器的目的。所以答案为 20