用闭包实现缓存函数的核心是外层函数创建私有Map缓存,内层函数通过作用域链访问;支持任意参数类型作key,多参数用JSON.stringify(args)生成key,需确保函数纯正、避免副作用,并建议添加LRU容量控制。

用闭包实现缓存函数,核心是让函数“记住”自己算过的值——外层函数创建私有缓存容器,内层函数通过作用域链持续访问它。相同输入直接返回结果,跳过重复计算,性能提升立竿见影。
用 Map 封装私有缓存,支持任意参数类型
普通对象作缓存时,数组、对象当参数会转成 [object Object],导致键冲突或全部失效。Map 没这个问题,能直接用数组、对象、NaN 甚至函数当 key。
- 简单场景:单个字符串或数字参数,可直接作为 Map 键,无需序列化
- 多参数或含对象:推荐
JSON.stringify([a, b, c])生成 key,够快也够稳 - 遇到
undefined、Date、循环引用等,JSON.stringify会丢数据或报错,此时改用structuredClone(现代环境)或轻量比较库如fast-deep-equal
只对纯函数缓存,避免掩盖逻辑错误
缓存生效的前提是:输入相同,输出一定相同,且不修改外部状态。一旦函数里有这些行为,缓存就危险:
- 调用
Date.now()、Math.random()、performance.now() - 发起网络请求(
fetch)、读写localStorage - 修改全局变量、push 到外部数组、触发 DOM 更新
加了缓存后表面变快,实则第二次调用可能没发请求、没更新界面,问题更难排查。
立即学习“Java免费学习笔记(深入)”;
高频场景必须加容量控制,防内存爆炸
Canvas 渲染、滚动监听、鼠标移动等每秒可能调用上百次。无节制缓存会让 Map 持续膨胀,拖慢 GC、引发卡顿。
- 手动实现 LRU:设上限(如 100 条),每次
set前检查cache.size,超限就删最早 entry - 直接用
lru-cache库,它已内置max、ttl、dispose回调等能力 - 对固定组合参数(如
canvas.width + scale),可设计“参数变化即清空缓存”的条件刷新逻辑
封装成通用 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(...args);
cache.set(key, result);
return result;
};
}
- 返回新函数,缓存生命周期与该实例绑定,多个
memoize(fn)调用互不影响 - 支持多参数(用
...args),兼容多数函数签名 - 若需保持
this上下文,可用fn.apply(this, args)替代fn(...args)



















