闭包是实现函数记忆化的最自然方式,通过内部函数持久引用外部变量缓存结果,避免重复计算;基础版用Object键需JSON序列化,增强版用Map支持任意类型参数,但需注意内存管理和参数相等性。

闭包是实现函数结果缓存(记忆化)最自然、最常用的方式。它的核心在于:利用内部函数对外部变量的持久引用,把计算结果存在闭包作用域中,后续调用直接复用,避免重复执行。
闭包记忆化的关键结构
一个记忆化函数本质上是一个“工厂函数”:它接收目标函数作为参数,返回一个新函数。这个新函数通过闭包持有缓存对象,并在每次调用时检查、读取或写入缓存。
- 外部函数定义并维护缓存容器(如 Object 或 Map)
- 返回的内部函数负责参数处理、缓存查找与原函数调用
- 内部函数能持续访问外部函数的缓存变量,即使外部函数早已执行完毕
基础实现:用 Object 缓存简单参数
适用于参数可安全序列化(如数字、字符串、简单对象)的场景:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) {
return cache[key];
}
const result = fn(...args);
cache[key] = result;
return result;
};
}
- cache 在外层函数作用域中声明,被返回的函数“记住”
- 每次调用都先查 cache[key],命中则跳过计算
- 注意:JSON.stringify 对函数、undefined、Symbol、循环引用等会出错或丢失信息
增强实现:用 Map 支持更多类型参数
解决 Object 键限制问题,支持任意类型(包括对象、函数、Symbol)作为参数:
立即学习“Java免费学习笔记(深入)”;
function memoize(fn) {
const cache = new Map();
return function(...args) {
// 用 args 数组本身作键(需保证引用唯一性)
// 更稳妥做法:用 WeakMap + 参数哈希,或封装为键生成器
const key = args.length === 1 ? args[0] : args;
const cached = cache.get(key);
if (cached !== undefined) {
return cached;
}
const result = fn(...args);
cache.set(key, result);
return result;
};
}
- Map 允许任意值作键,更健壮;但要注意引用相等性判断
- 若参数是普通对象,建议用 JSON.stringify 或自定义哈希函数生成唯一 key
- 高频调用场景下,WeakMap 可配合对象参数减少内存泄漏风险
实际应用中的注意事项
记忆化不是万能药,闭包带来的便利也伴随隐含成本:
- 缓存占用内存,长期运行需考虑清理策略(如 LRU、TTL 或手动 clear)
- 副作用函数(如修改全局状态、发请求)不适合直接记忆化,除非你明确要“冻结”其行为
- 参数顺序、类型、相等性逻辑必须清晰,否则缓存可能误命中或漏命中
- 递归函数(如斐波那契)加记忆化后,性能提升显著,但要注意闭包捕获的是同一份 cache

















