闭包实现缓存的核心是利用外层函数创建私有持久的map缓存,内层函数通过作用域链访问;键需稳定序列化(如json.stringify),仅纯函数适用,需处理异常清除与this绑定。

闭包实现缓存计算结果,本质是让函数“记住”自己算过的值,相同输入直接返回,跳过重复运算。关键不在写个 wrapper,而在缓存怎么存、键怎么定、边界怎么控。
闭包提供私有且持久的缓存空间
外层函数定义一个缓存容器(比如 Map),内层函数通过作用域链持续访问它。这个缓存不会被垃圾回收,也不被外部干扰,每次调用 memoize(fn) 都生成独立缓存实例。
- 用 Map 而不是普通对象:支持任意类型键(数字、字符串、对象、函数),避免
toString()冲突或原型污染 - 缓存变量声明在闭包内,外部无法直接读写,封装性强
- 返回的新函数始终持有对缓存的引用,状态得以维持
参数要能稳定转成唯一缓存键
缓存是否命中,取决于键是否一致。原始参数(尤其是对象、数组)不能直接当键用,必须序列化或归一化。
- 单个基础类型参数(如数字、字符串)可直接作为 Map 键,无需处理
- 多个参数或含对象时,推荐
JSON.stringify([a, b, c]),简单场景够用 - 遇到
undefined、function、Symbol或循环引用,JSON.stringify会丢失或报错,此时需改用structuredClone(现代环境)或轻量比较库(如fast-deep-equal) - 键生成逻辑必须确定:相同输入永远产出相同字符串,顺序、字段名、嵌套结构都要一致
得兼顾纯函数前提和常见陷阱
缓存只对纯函数有效——输入相同,输出一定相同,且不修改外部状态。
- 若函数依赖全局变量、时间、随机数或 DOM 状态,缓存结果可能失效
- 注意
this绑定问题:缓存函数内部调用原函数时,需用fn.apply(context, args)显式传入上下文 - 错误不能缓存:捕获异常后应清除对应缓存项,避免下次调用直接抛出旧错误
- 长期运行的页面要考虑缓存清理机制,比如按数量上限淘汰,或设置 TTL 过期时间
实际写法示例(精简可靠版)
以下是一个生产可用的 memoize 实现,兼顾健壮性与可读性:
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
try {
const result = fn.apply(this, args);
cache.set(key, result);
return result;
} catch (err) {
cache.delete(key);
throw err;
}
};
}
调用方式:const fib = memoize(n => n
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











