提到js中函数计算结果的缓存,首先应该想到的应该是闭包,因为正常情况下,执行过的函数和被使用完且不再被引用的变量会被垃圾回收机制销毁掉。想要缓存结果,则必须将其放在闭包内,使其不会被垃圾回收机制回收。
闭包:可以在另一个作用域中调用一个函数的内部函数,并在内部函数中访问外部函数作用域当中的成员。
举个例子:
使用过jQuery的小伙伴们应该知道jq当中的once方法,他的作用是传入并返回一个函数,并确保传入的函数只执行一次,其中就用到了闭包实现变量缓存的方法,我们先来实现一下。
let once = (fn) => {
let done = false;
return function() {
if(!done) {
done = true;
fn.apply(this, arguments)
}
}
}
运行结果:
image.png
我们可以看到,传入的foo函数即使调用多次,也只执行了一次。这就说明外部函数中的done变量被函数缓存了。
Lodash是前端常用的js库,其中使用函数式编程的方式为我们提供了很多方便的方法和工具,在ECMA的后续更新中也时常借鉴其中的方法,并将其拓展为js的新特性。其中memoize结果缓存方法和上面提到的方法也很类似:
let memoize = (fn) => {
let cache = {};
return function() {
let key = JSON.stringify(arguments)
cache[key] = cache[key] || fn.apply(this, arguments)
return cache[key]
}
}
上面我们把传入函数的第一次执行结果存储到闭包外部函数的cache对象中,调用时如果发现已经有缓存结果则直接返回之前的缓存结果,避免不必要的函数计算。
注意: 这里前提是函数式编程,传入的函数必须是“纯函数”。
纯函数:传入同样参数则返回同样的值。
在上面封装的memoize方法中,我们直接用传入的参数arguments为key,在纯函数中同样的arguments返回的值也是相同的,因此我们可以以此来进行缓存,当我们传入不同的arguments时,也不影响memoize方法进行重新计算。