JS的装饰器模式实例分析

479 阅读3分钟

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

装饰器模式

假设我们有一个的函数 slow(x),对于相同的x,它返回的是相同的结果。

function slow(x) {
 // 假设这里有很多计算
 //....
  return x;
}

如果经常调用该函数,我们可能希望将结果缓存(记住)下来,以避免在重新计算上花费额外的时间。

可以思考一下如果让你处理这个问题,会怎么做?

对于这个问题,你或许可以在slow() 函数中添加一个缓存结果的功能来实现需求,但是还可以有一种更为优雅的方式来解决,那就是创建一个增加了缓存功能的包装器(wrapper)函数来处理。该函数如下:

function cachingDecorator(func) {
  let cache = new Map();

  return function(x) {
    if (cache.has(x)) {    // 如果缓存中有对应的结果
      return cache.get(x); // 从缓存中读取结果
    }

    let result = func(x);  // 否则就调用 func

    cache.set(x, result);  // 然后将结果缓存(记住)下来
    return result;
  };
}

现在我们来执行看看

image-20211110230322270

这个示例中,有slow cachingDecorator 两个函数:

  • cachingDecorator 是一个 装饰器(decorator):一个特殊的函数,它接收slow 函数并且改变了它的行为,使它拥有了缓存的功能。

  • 而对于slow函数,从外部代码来看,包装的 slow 函数执行的仍然是与之前相同的操作。它只是在其行为上添加了缓存功能。

通过这种方式,将缓存与主函数代码分开,不但可以不改变 slow 本身的代码来实现缓存功能,还可以为任何函数调用 cachingDecorator,来实现同样的功能。

call

上面我们利用装饰器模式,实现了函数结果的缓存,现在假设我们有这样一个对象方法

let worker = {
  someMethod() {
    return 1;
  },

  slow(x) {
    console.log('当前的this是',this) //执行的时候,打印一下当前的this
    return x * this.someMethod();
  }
};

我们同样想要实现缓存功能,来对 worker.slow 的结果进行缓存

image-20211110234343610

可以看到,正常调用是可以的,但是使用了装饰器包装之后,虽然也调用了该方法,但是this的指向却变了,从而导致在试图访问 this.someMethod 时报错。

所以对于这种对象方法,我们上面的cachingDecorator不再适用,这个时候,我们可以用一个特殊的内置函数方法 func.call(context, …args)(👉传送门)来处理,它允许调用一个显式设置 this 的函数。

现在我们来改写一下前面的cachingDecorator函数

function cachingDecorator(func) {
  let cache = new Map();
  return function(x) {
    if (cache.has(x)) {
      return cache.get(x);
    }
    let result = func.call(this, x); // 重点是这句, 把"this" 正确的传递
    cache.set(x, result);
    return result;
  };
}

这个时候可以看到,执行结果都正常了。

image-20211110235440676

参考资料:

Decorators and forwarding


🎨【点赞】【关注】不迷路,更多前端干货等你解锁

往期推荐

👉 前端生成二维码和条形码完整方案

👉 不能不知道的15 个JavaScript数组实用技巧

👉 给开发看的UI设计

👉 WebStorm提高工作效率的实用配置