You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何缓存接收含对象在内的多个参数的JavaScript函数?

实现支持含对象参数的函数缓存(Memoize)

普通的缓存实现(比如直接用Map存参数组合)会因为对象的引用特性失效——即使两个对象内容完全一致,只要是不同的引用,就会被当成不同的参数,导致缓存无法复用。针对你的场景,这里提供几种实用的解决方案:

方案1:序列化参数生成缓存键(通用场景)

如果你的参数对象没有循环引用、函数或Symbol这类无法被JSON序列化的内容,可以用JSON.stringify把所有参数转换成字符串作为缓存键,实现通用的memoize函数:

function memoize(fn) {
  const cache = new Map();
  
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

使用示例

function expensiveCalculation(a, b, options) {
  console.log('执行耗时计算');
  return a + b + options.value;
}

const memoizedCalculation = memoize(expensiveCalculation);

const options = { value: 10 };
console.log(memoizedCalculation(5, 3, options)); // 输出"执行耗时计算"和18
console.log(memoizedCalculation(5, 3, options)); // 直接输出18(复用缓存)
// 内容相同的新对象也能复用缓存
console.log(memoizedCalculation(5, 3, { value: 10 })); // 直接输出18

注意事项

  • 缺点:JSON.stringify有一定性能开销,参数过大或调用极频繁时需要权衡
  • 局限性:无法处理含函数、循环引用、Symbol的对象;对象属性顺序不同会生成不同键(比如{a:1,b:2}和{b:2,a:1}会被当成不同参数)

方案2:序列化时忽略对象属性顺序

如果需要让属性顺序不同但内容一致的对象被识别为相同参数,可以自定义序列化逻辑,先对对象的键排序再序列化:

function serializeArgs(args) {
  return JSON.stringify(args, (key, value) => {
    if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
      // 排序对象键,确保属性顺序不影响缓存键
      return Object.fromEntries(Object.entries(value).sort());
    }
    return value;
  });
}

function memoize(fn) {
  const cache = new Map();
  
  return function(...args) {
    const key = serializeArgs(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

测试示例

console.log(memoizedCalculation(5, 3, { value:10, other:20 })); // 执行耗时计算,输出38
console.log(memoizedCalculation(5, 3, { other:20, value:10 })); // 直接输出38(复用缓存)

方案3:用WeakMap缓存固定引用的对象

如果你的参数对象是固定引用(比如始终传入同一个options对象),可以用WeakMap做缓存——它会在对象被垃圾回收时自动清理缓存项,避免内存泄漏:

function memoizeWithWeakMap(fn) {
  // 多层缓存:第一层存基本类型参数组合,第二层存对象的WeakMap
  const cache = new Map();
  
  return function(a, b, options) {
    const baseKey = `${a}-${b}`;
    if (!cache.has(baseKey)) {
      cache.set(baseKey, new WeakMap());
    }
    const objCache = cache.get(baseKey);
    
    if (objCache.has(options)) {
      return objCache.get(options);
    }
    const result = fn(a, b, options);
    objCache.set(options, result);
    return result;
  };
}

使用示例

const memoizedCalculation = memoizeWithWeakMap(expensiveCalculation);
const options = { value:10 };
console.log(memoizedCalculation(5,3,options)); // 执行耗时计算,输出18
console.log(memoizedCalculation(5,3,options)); // 复用缓存,输出18
// 注意:新对象即使内容相同,也会重新计算
console.log(memoizedCalculation(5,3,{ value:10 })); // 执行耗时计算,输出18

方案选择建议

  • 通用场景优先选方案2,能覆盖大多数普通对象参数的情况
  • 如果参数对象是固定引用,选方案3更节省内存
  • 性能要求极高时,可以给对象添加唯一标识(比如id属性),用标识作为缓存键的一部分

内容的提问来源于stack exchange,提问作者sonu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:23:14