「这是我参与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;
};
}
现在我们来执行看看
这个示例中,有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 的结果进行缓存
可以看到,正常调用是可以的,但是使用了装饰器包装之后,虽然也调用了该方法,但是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;
};
}
这个时候可以看到,执行结果都正常了。
参考资料:
🎨【点赞】【关注】不迷路,更多前端干货等你解锁
往期推荐