结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

使用场景

传统增强函数或类的方法:

  1. 传入新的参数,容易导致代码堆积大量的 ifswitch语句
  2. 使用继承,容易产生大量字类

使用装饰器的原因:

  1. 希望在无需修改代码的情况下即可使用对象,且希望在运行时为对象新增额外的行为

    if you want to do a thing, but before you do that, you must do other things and you don’t know how many thing you should do, than decorator pattrern can decorate a thing you want to do!

代码实现

  1. class Printer {
  2. print(text, style = '') {
  3. console.log(`%c${text}`, style);
  4. }
  5. }
  6. // decorator method
  7. const yellowStyle = (printer) => ({
  8. ...printer,
  9. print: (text) => {
  10. printer.print(text, 'color: yellow;');
  11. }
  12. });
  13. const boldStyle = (printer) => ({
  14. ...printer,
  15. print: (text, style = '') => {
  16. printer.print(text, `${style}font-weight: bold;`);
  17. }
  18. });
  19. const bigSizeStyle = (printer) => ({
  20. ...printer,
  21. print: (text, style = '') => {
  22. printer.print(text, `${style}font-size: 36px;`);
  23. }
  24. });

class decorators

The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.

  1. const addFuelToRocket = (target: Function) => {
  2. return class extends target {
  3. fuel = 100
  4. }
  5. }
  6. @addFuelToRocket
  7. class Rocket {}
  8. const rocket = new Rocket()
  9. console.log((rocket).fuel) // 100

Method decorators

The decorator is applied to the Property Descriptor for the method, and can be used to observe, modify, or replace a method definition.

  1. const myDecorator = (target: Object, propertyKey: string, descriptor: PropertyDescriptor) => {
  2. // do something with your method
  3. }
  4. class Rocket {
  5. @myDecorator
  6. launch() {
  7. console.log("Launching rocket in 3... 2... 1... 🚀")
  8. }
  9. }
  • target:该类的原型对象,即 Rocket.prototype
  • propertyKey:所装饰函数名
  • descriptor:该属性的描述对象,target.value 指的是该类方法本身

    实际场景下的使用

    接口逻辑中的实践

    接口时间监控

    ```javascript // 装饰器函数 function logTime(apiId) { return function(target, key, descriptor) { const originFunc = descriptor.value; descriptor.value = function() {
    1. const startTime = new Date().valueOf();
    2. return originFunc.apply(this, arguments).then(res => {
    3. const endTime = new Date().valueOf();
    4. const spendTime = endTime - startTime;
    5. console.log('apiId: ',apiId);
    6. console.log('spendTime: ', spendTime)
    7. // 向后台发送一些数据监控
    8. // logApi.logData(apiName, spendTime);
    9. return res;
    10. }).catch(err => {
    11. throw err
    12. })
    } return descriptor } }

// 使用装饰器 class UserApi { @logTime(‘ididid’) static setUserName(name) { return setUserName(name) } }

// 使用接口 UserApi.setUserName(‘vince’)

  1. <a name="DgP7W"></a>
  2. ### 接口 Toast 提醒
  3. ```javascript
  4. // 装饰器函数
  5. function operateToast(successInfo = '操作成功', errorInfo = '操作失败,请重试') {
  6. return function (target, key, descriptor) {
  7. const originFunc = descriptor.value;
  8. descriptor.value = function() {
  9. return originFunc.apply(this, arguments).then(res => {
  10. toast(successInfo)
  11. return res
  12. }).catch(err => {
  13. toast(errorInfo)
  14. throw err
  15. })
  16. }
  17. return descriptor
  18. }
  19. }
  20. // 使用装饰器
  21. class UserApi {
  22. @operateToast('设置用户名称成功', '后端太垃圾了,设置用户名称接口挂了')
  23. static setUserName(name) {
  24. return setUserName(name)
  25. }
  26. @operateToast('设置用户年龄成功', '后端太垃圾了,接口又挂了')
  27. static setUserAge(age) {
  28. return setUserAge(age)
  29. }
  30. }
  31. // 调用接口
  32. UserApi.setUserName('vince')
  33. UserApi.setUserAge(12)

接口容错发送

  1. // 装饰器函数
  2. function retryFunc(counts, times) {
  3. return function(target, name, descriptor) {
  4. const originFunc = descriptor.value;
  5. descriptor.value = function() {
  6. let count = 1;
  7. return new Promise((resolve, reject) => {
  8. const retry = () => {
  9. console.log('开始请求')
  10. return originFunc().then(res => {
  11. resolve(res);
  12. }).catch(() => {
  13. count++;
  14. if (count > counts) {
  15. reject(new Error('多次请求错误,请稍后再试'));
  16. return;
  17. }
  18. console.log(`请求失败,第${count}次重试`)
  19. setTimeout(() => {
  20. retry();
  21. }, times)
  22. })
  23. }
  24. retry();
  25. })
  26. }
  27. return descriptor
  28. }
  29. }
  30. // 使用装饰器
  31. class UserApi {
  32. @retryFunc(3, 1000)
  33. static setUserName(name) {
  34. return setUserName(name)
  35. }
  36. }
  37. // 调用接口
  38. UserApi.setUserName('vince')

参考资料

  1. 装饰模式
  2. JavaScript design patterns #4. Decorators and their implementation in TypeScript
  3. What is the decorator pattern? And how to implement it by JavaScript?
  4. Decorator (装饰器)入门以及在前端接口逻辑层中的实践
  5. A practical guide to TypeScript decorators
  6. ts 官方文档