JavaScript缓存函数的核心是用对象或Map缓存参数组合与结果,相同输入直接返回缓存值;适用于纯函数如斐波那契、阶乘等;基础版支持单基本类型参数,增强版用JSON.stringify处理多参但有局限,生产级推荐Map加自定义键生成器,并注意纯函数约束、内存泄漏和异步缓存等问题。

JavaScript中实现缓存函数(即记忆化,Memoization)的核心思路是:**用对象或Map缓存已计算过的参数组合与对应结果,下次遇到相同输入时直接返回缓存值,跳过重复计算**。它特别适合纯函数(相同输入总得相同输出、无副作用),比如斐波那契、阶乘、解析复杂JSON结构等场景。
基础版:单参数 + 字符串键
最简单的情况是函数只接收一个基本类型参数(如 number、string)。这时可直接用参数作键:
function memoize(fn) {
const cache = {};
return function(arg) {
if (arg in cache) {
return cache[arg];
}
const result = fn(arg);
cache[arg] = result;
return result;
};
}
// 使用示例
const fib = memoize(n => n <= 1 ? n : fib(n - 1) + fib(n - 2));
console.log(fib(10)); // 快速返回 55
- ✅ 简单直观,适合数字/字符串等可安全作为对象属性名的参数
- ❌ 不支持对象、数组、undefined、Symbol 等无法稳定转为字符串的值(例如
{a:1}和{a:1}会变成相同字符串"[object Object]")
增强版:多参数 + JSON.stringify(慎用)
多个参数时,常用 JSON.stringify([...arguments]) 生成唯一键:
function memoizeMulti(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
}
- ✅ 支持任意数量参数,对普通对象/数组也能工作(只要可序列化)
- ❌
JSON.stringify有局限:忽略函数、undefined、Symbol;不区分{a:1,b:2}和{b:2,a:1}(属性顺序影响键);性能开销略大;不能处理循环引用
生产级:使用 Map + 自定义键生成器
更健壮的做法是用 Map 存储,并为参数设计稳定、可比较的键。常见策略包括:
立即学习“Java免费学习笔记(深入)”;
- 对每个参数用
typeof判断后分别处理:基本类型直接用,对象用Object.is或===比较引用,或用弱映射(WeakMap)缓存对象实例 - 使用现成库如
lodash.memoize(支持自定义resolver函数) - 手动实现带参数归一化的版本(例如把对象参数转为排序后的键字符串,或用结构化克隆+哈希,但成本高)
简化的安全多参版(适用于无嵌套对象的场景):
function memoizeSafe(fn) {
const cache = new Map();
return function(...args) {
// 用参数类型+值拼接成唯一键(避免 JSON 的坑)
const key = args.map(a =>
typeof a === 'object' && a !== null
? `${typeof a}_${a.constructor.name}_${Object.keys(a).sort().join(',')}`
: `${typeof a}_${String(a)}`
).join('|');
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;};
}
注意事项与边界
记忆化不是万能的,用前需确认:
- 函数必须是纯函数——不能依赖外部变量、不能修改全局状态、不能有随机性
- 注意内存泄漏:缓存无限增长?建议加最大容量限制或 LRU 策略(可用
lru-cache或自己封装 Map + 链表) - 异步函数需配合 Promise 缓存(缓存的是 Promise 实例本身,不是最终值,避免重复发起请求)
- 箭头函数、this 绑定:memoize 包裹后会丢失原始 this,如需保留,应在闭包内显式绑定
基本上就这些。记住核心:缓存是空间换时间,关键在“键要唯一且稳定”,其余都是围绕它做取舍。










