什么是JavaScript Memoization_它如何加速函数计算?

JavaScript Memoization 是手动实现的缓存策略,通过 Map 存储参数序列化后的输入输出映射,避免重复计算;但需注意对象/数组键处理、适用场景及性能权衡。

JavaScript Memoization 不是语言内置功能,而是一种手动实现的缓存策略:对相同输入反复调用函数时,跳过重复计算,直接返回之前缓存的结果。

memoize 函数怎么写?

核心逻辑很简单:用一个对象(或 Map)存 input → output 映射。每次调用前先查缓存,命中就返回;没命中就执行原函数、存结果、再返回。

常见错误是把对象或数组当 key 直接用 —— 它们会被转成 "[object Object]",导致所有对象输入都撞同一个缓存项。

  • 只对原始类型(stringnumberbooleansymbol)做简单键生成
  • 对对象/数组,建议用 JSON.stringify(注意:不能处理函数、undefined、循环引用)
  • 生产环境推荐用 lodash.memoizefast-memoize,它们已处理了深比较、this 绑定、maxSize 限制等问题
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;
  };
}

为什么 useMemo 和 memo 不是 Memoization?

useMemoReact.memo 是 React 的优化手段,但和传统 Memoization 有本质区别:

  • useMemo 只在依赖数组变化时重新计算,不保证「相同输入必得相同输出」—— 它不校验参数值,只看依赖引用是否变
  • React.memo 是对组件的浅层 props 比较,跳过 render,不是函数计算缓存
  • 它们都不跨渲染周期保留结果(比如组件卸载后缓存即丢),也不支持自定义键生成逻辑

什么时候用 Memoization 反而更慢?

缓存本身有开销:序列化参数、查表、内存占用。如果函数本身极快(如 a + b),或输入几乎从不重复,加 memoize 就是负优化。

  • 适合:高耗时、纯函数、输入重复率高(如递归斐波那契、解析大型 JSON Schema、坐标系转换)
  • 不适合:I/O 操作(缓存可能过期)、带副作用的函数、单次调用场景
  • 注意副作用泄漏:如果原函数修改了外部变量,缓存后再次调用会跳过该修改,行为不一致

真正难的不是写个 memoize,而是判断哪些函数值得缓存、缓存多久、要不要自动失效——这些得结合业务数据特征来定,没法靠工具自动解决。