使用闭包实现缓存记忆(memoization)

缓存记忆让函数能够记录它之前被调用时所产生的结果。这样,我们第二次获取结果值时,优先从函数的缓存记录中查找结果,避免重复计算结果。

首先要实例一个无闭包的版本:

  • #1 在value属性中保存函数的返回值,如果没有就创建
  • #2 传入key进行调用的时候,检测是否有该key对应的缓存,如果有就直接返回,否则调用函数,并将结果保存在value属性中,以便下次使用。
  • #3 使用素数计算函数作为测试

运行结果:
memoization_without_closure

直接切入现在函数的能力是有限的,但我们可以很容易在函数上添加一个新的方法,或者通过 prototype 给所有函数都添加一个新方法。上述代码最主要的地方是,函数计算与结果保存都是在一个单独的步骤中。计算结果是通过apply()调用父函数来完成的,并直接保存在数据存储对象中。但这行语言中包含了return关键字,也就是说结果也直接返回给了父函数。所以整个事件链:计算结果,保存结果,返回结果——都是在一个逻辑代码单元中完成的。

这种方式的缺点是,isPrime()函数的调用者也必须要调用memoized()方法才能使用缓存记忆功能,但这往往不太现实。

接下来,让我们想想如何使用闭包来改进缓存记忆的功能,以便在函数调用时自己进行缓存记忆,而无需调用memoized()之类的方法。代码如下:

  • #1 通过变量赋值将上下文(this)带入闭包中,否则上下文就会丢失,因为它不会成为闭包的一部分
  • #2 在缓存记忆函数中封装原始的函数

运行结果:
memoization_with_closure

从结果中可以看到,只在第一次调用isPrime(5)进行了计算,第二次及后面的调用都未进行计算,都是从缓存中获取的结果。并且兼容第一个例子中的写法isPrime.memoized(5),进行缓存操作的函数memoized()未做任何修改,我们对isPrime()函数进行了改造,使缓存功能与函数绑定,无需使用者调用。

memoize()中,我们构建一个闭包,通过将上下文复制到一个变量中,从而记住需要添加缓存记忆功能的原始函数(通过上下文 this)。这是一种常用的技巧:每个函数都有自己的上下文,所以函数的上下文(this)从来都不是闭包的一部分。但是可以通过创建一个变量引用到上下文,从而将上下文变成闭包的一部分。

发表评论