You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

JavaScript实现memoize函数时闭包引发缓存频繁清空的问题排查

为什么你的memoize缓存每次都被清空?

我来帮你拆解一下问题的核心——你的代码里的cache之所以每次调用都显示为空,本质是每次执行fib(n)的时候,你都重新调用了memoize(calcF),生成了一个全新的闭包实例,每个闭包都带着自己独立的cache变量,完全没法共享缓存结果。

你的代码执行流程拆解

  1. 当你调用fib(10)时,函数内部立刻执行const m = memoize(calcF):这时候memoize会创建一个新的闭包,里面的cache是全新的空对象。
  2. 接着调用m(10),触发calcF(10)的计算,而calcF内部会递归调用fib(9)fib(8)……
  3. 每一次递归调用fib,都会再次执行memoize(calcF),生成新的闭包和新的空cache
  4. 结果就是每一层递归的缓存都是独立的,完全起不到复用的作用,所以你看到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

火山引擎 最新活动