闭包实现通用高阶函数参数缓存的关键是外层函数创建私有缓存空间并返回可读写该空间的内层函数,每个memoize(fn)调用生成独立new Map()缓存,键需稳定转换(如JSON.stringify),且仅适用于纯函数。

闭包实现通用高阶函数的参数缓存,关键在于用外层函数创建私有缓存空间,再返回一个能读写该空间的内层函数。它不是给函数“加个缓存开关”,而是让每次调用都自带记忆能力。
缓存容器必须独立且受控
每个 memoize(fn) 调用都要生成专属缓存,不能共用全局对象——否则不同函数会互相污染。
- 推荐用
new Map()存储,支持任意类型键(对象、数组、函数等),避免对象键隐式转字符串导致冲突 - 缓存变量声明在高阶函数内部,外部无法直接访问或修改,封装性好
- 返回的新函数通过闭包持续持有对这个
Map的引用,生命周期与该函数实例一致
参数到缓存键的转换要稳定可靠
缓存是否命中,完全取决于键是否一致。原始参数不能直接当键用,尤其含对象、数组、undefined 或 Symbol 时。
- 单个基础类型(数字、字符串)可直接作 Map 键
- 多个参数或含对象时,用
JSON.stringify([a, b, c])简单有效,但要注意:undefined、function、Symbol 会被忽略,循环引用会报错 - 生产环境建议用
structuredClone配合轻量序列化逻辑,或引入fast-deep-equal做结构比对代替字符串化
必须确保被缓存函数是纯函数
缓存只对纯函数有意义——相同输入永远返回相同输出,且不依赖或修改外部状态。
立即学习“Java免费学习笔记(深入)”;
- 如果函数内部读取了全局变量、Date.now()、Math.random() 或 DOM 状态,缓存结果可能失效
- 避免缓存异步操作(如 fetch),除非你明确控制请求时机和响应一致性
- 可加简单错误捕获:缓存前先 try/catch,失败时不写入缓存,避免存坏结果
基础实现示例(带键归一化)
以下是一个兼顾可用性与健壮性的最小可行版本:
function memoize(fn) {
const cache = new Map();
return function(...args) {
// 简单键生成:基础类型直接用,复杂结构用 JSON.stringify
const key = args.length === 1 && (typeof args[0] === 'object' || Array.isArray(args[0]))
? JSON.stringify(args[0])
: JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
它适用于多数同步计算场景,比如格式化字符串、坐标转换、树节点查找等。更复杂需求(如 TTL 过期、LRU 淘汰)需在此基础上扩展。


















