JavaScript实现memoize函数时闭包引发缓存频繁清空的问题排查
为什么你的memoize缓存每次都被清空?
我来帮你拆解一下问题的核心——你的代码里的cache之所以每次调用都显示为空,本质是每次执行fib(n)的时候,你都重新调用了memoize(calcF),生成了一个全新的闭包实例,每个闭包都带着自己独立的cache变量,完全没法共享缓存结果。
你的代码执行流程拆解
- 当你调用
fib(10)时,函数内部立刻执行const m = memoize(calcF):这时候memoize会创建一个新的闭包,里面的cache是全新的空对象。 - 接着调用
m(10),触发calcF(10)的计算,而calcF内部会递归调用fib(9)、fib(8)…… - 每一次递归调用
fib,都会再次执行memoize(calcF),生成新的闭包和新的空cache。 - 结果就是每一层递归的缓存都是独立的,完全起不到复用的作用,所以你看到
cache始终是空对象。
为什么把cache移到外部就有效?
当你把let cache = {}放到memoize函数外部时,这个cache就变成了外层作用域的共享变量——所有memoize生成的闭包都会访问同一个cache,递归时计算的结果会被统一存到这个缓存里,自然就能复用之前的计算结果了。
正确的修正方案
这里提供两种常见的有效写法:
方案1:提前缓存memoize的结果
把带缓存的calcF实例提前创建好,而不是在fib内部每次递归都新建:
function calcF(n) { if (n === 0) { return 0; } if (n === 1) { return 1; } return fib(n - 1) + fib(n - 2); } function memoize(cb) { let cache = {}; return function (arg) { console.log(cache); if (cache[arg]) { return cache[arg]; } else { const res = cb(arg); cache[arg] = res; return res; } }; } // 提前创建带缓存的calcF实例,所有递归调用共享它 const memoizedCalcF = memoize(calcF); function fib(n) { return memoizedCalcF(n); } console.time(); console.log(fib(10)); console.timeEnd();
方案2:用自执行闭包封装缓存
把缓存和递归逻辑封装在同一个闭包里,确保缓存只初始化一次:
const fib = (function() { const cache = {}; function calcF(n) { if (n === 0) { return 0; } if (n === 1) { return 1; } return fib(n - 1) + fib(n - 2); } return function(n) { if (cache[n]) return cache[n]; const res = calcF(n); cache[n] = res; return res; }; })(); console.time(); console.log(fib(10)); console.timeEnd();
关键知识点总结
- 闭包的作用域是和它被创建时的上下文绑定的,每次调用
memoize都会生成一个全新的闭包,拥有独立的内部变量。 - 要实现有效的函数记忆(memoization),必须让所有相关的函数调用共享同一个缓存实例,而不是每次调用都新建缓存。
内容的提问来源于stack exchange,提问作者Rahul




