javascript中用闭包实现函数缓存的核心是利用闭包持久化私有map缓存,查命中则返回,未命中则执行并存储;推荐map因支持任意键类型和精确相等判断,避免对象键隐式转换误命中;memoize函数需处理键生成、this绑定、手动清空及纯函数约束。

JavaScript 中用闭包实现函数缓存,核心在于利用闭包捕获并持久化一个私有缓存对象(如 Map 或普通对象),让每次调用前先查缓存、命中则直接返回,未命中则执行原函数并存入缓存。这种方式轻量、无依赖、不污染全局,适合纯函数或确定性计算场景。
缓存结构选 Map 还是普通对象?
推荐优先使用 Map:支持任意类型键(如对象、函数、Symbol)、可精确判断键相等性、天然避免原型链干扰,且能动态追踪缓存大小。普通对象仅适合字符串/数字键,且容易因 toString() 隐式转换导致误命中(比如 {} 和 new Date() 都变成 "[object Object]")。
示例中缓存键建议用 JSON.stringify(args) 仅限简单参数(全为基本类型、无 undefined / function / NaN);更健壮的做法是用 args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join('|') 做简易序列化,或引入 fast-deep-equal 类库做深度比对(但会增加体积)。
如何封装一个通用的缓存高阶函数?
写一个 memoize 函数,接收目标函数和可选配置(如最大缓存数、是否忽略 this 上下文),返回带缓存能力的新函数:
- 内部用闭包维持
cache = new Map()和可选的 LRU 驱逐逻辑(如用cache.keys().next().value获取最早插入项) - 执行时生成唯一键:若忽略
this,可用JSON.stringify([args]);若需绑定上下文,建议将this与参数一起参与键生成(如JSON.stringify([this, ...args])) - 支持手动清空缓存(暴露
.clear()方法)或按需失效(如传入keyGenerator回调自定义键)
注意副作用与缓存失效边界
闭包缓存只适用于纯函数:相同输入必得相同输出,且不依赖外部可变状态(如时间、随机数、DOM、全局变量)。一旦函数内读取了 Date.now() 或 Math.random(),缓存就不可靠。
常见陷阱包括:
- 参数含引用类型(如数组、对象)但内容变化后仍被当同一键——需深比较或序列化前克隆
- 异步函数直接缓存 Promise 实例,导致后续调用拿到“正在 pending”的同一个 Promise,而非新请求
- 缓存无限增长:未设上限时,高频多参调用可能撑爆内存,建议配合
maxSize与 LRU 策略
一个精简可用的实现参考
以下是一个兼顾可读性与实用性的最小可行版本(无 LRU,支持基本参数):
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;
};
}
使用:const fib = memoize(n => n —— 注意递归调用必须用缓存后的函数名,否则不生效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











