什么是记忆函数
记忆函数是一种常用的技术,可以显著加快你的代码执行速度。它依赖于一个缓存来存储之前已完成工作单元的结果。缓存的目的是避免重复执行相同的工作,从而加快耗时函数的后续调用
记忆函数的使用
根据其定义,我们可以很容易地推导出一些标准来帮助我们发现适合记忆化的好候选者:
- 执行缓慢、成本高昂或耗时的函数调用可以从记忆化中受益
- 记忆化可以加快后续调用,因此当您预期在相同情况下多次调用同一函数时,使用记忆化效果最佳
- 结果存储在内存中,因此当在非常不同的情况下多次调用同一函数时,应避免使用记忆化
记忆函数实现
在JavaScript中,自己编写一个记忆化函数是相当简单的。对于这个实现,我们将使用Map来存储结果。Map对象保存键值对,并记住键的原始插入顺序。这使得它非常适合用于记忆化,因为我们可以使用函数的参数作为键,将结果作为值。
const memoize = fn => {
const cache = new Map();
const cached = function (val) {
return cache.has(val)
? cache.get(val)
: cache.set(val, fn.call(this, val)) && cache.get(val);
};
cached.cache = cache;
return cached;
};
// This function is slow and will benefit from memoization
const anagrams = str => {
if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
return str
.split('')
.reduce(
(acc, letter, i) =>
acc.concat(
anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)
),
[]
);
};
const anagramsCached = memoize(anagrams);
anagramsCached('javascript');
// takes a long time
anagramsCached('javascript');
// returns virtually instantly since it's cached
使用Proxy 实现记忆函数
虽然前面的示例是实现记忆化的一个好方法,但JavaScript的Proxy对象通过使用handler.apply()陷阱提供了一个有趣的替代方案。
使用这个陷阱,我们可以拦截函数调用并检查结果是否已经被缓存。如果已经缓存,我们就返回缓存的结果。如果没有,我们就调用原始函数并在返回结果之前缓存它。
const memoize = fn => new Proxy(fn, {
cache: new Map(),
apply (target, thisArg, argsList) {
let cacheKey = argsList.toString();
if(!this.cache.has(cacheKey))
this.cache.set(cacheKey, target.apply(thisArg, argsList));
return this.cache.get(cacheKey);
}
});
const fibonacci = n => (n <= 1 ? 1 : fibonacci(n - 1) + fibonacci(n - 2));
const memoizedFibonacci = memoize(fibonacci);
for (let i = 0; i < 100; i ++)
fibonacci(30); // ~5000ms
for (let i = 0; i < 100; i ++)
memoizedFibonacci(30); // ~50ms
原创文章,作者:速盾高防cdn,如若转载,请注明出处:https://www.sudun.com/ask/82826.html