闭包通过在外部函数中定义缓存对象并返回访问该对象的递归函数,实现 memoization(记忆化),避免重复计算;适用于斐波那契、gcd、阶乘等纯递归场景,需注意缓存键生成、生命周期管理及纯函数约束。

闭包在递归函数中保存中间结果,核心是把“已算过的值”存在闭包内部的缓存对象里,后续相同输入直接返回,跳过重复递归分支。这叫 memoization(记忆化),对斐波那契、阶乘、GCD 等纯递归场景效果特别明显。
用闭包封装缓存对象,避免全局污染
把缓存(比如一个普通对象或 Map)定义在外部函数作用域内,返回的递归函数通过闭包访问它。这样每个 memoized 函数实例都有独立缓存,互不干扰:
- 适合单参数递归:用
cache[n]直接存取,简单高效 - 多参数时建议用
JSON.stringify([a, b, ...])生成键,或手写轻量拼接(如${a}_${b}),确保键唯一且可预测 - 不要把缓存挂到全局或 this 上,否则多个调用会互相覆盖或污染
ES6 写法:带缓存的斐波那契生成器
这是最典型的实战示例,清晰展示闭包如何拦截重复子问题:
- 第一次调
fib(5),会递归算fib(4)和fib(3) - 算
fib(4)时又需fib(3)和fib(2);此时fib(3)已被缓存,直接返回 - 后续再调
fib(5),所有子结果都在缓存中,变成 O(1) 查表
扩展到其他递归场景
只要函数满足“纯函数”特性(相同输入恒得相同输出),都可套用同一模式:
-
最大公约数(GCD):递归版
gcd(a, b) = gcd(b, a % b),缓存[a,b]键可避免重复模运算 -
阶乘(factorial):虽然线性递归本身不重叠,但若被高频调用(如渲染表格每行都算
n!),缓存仍能省下大量重复乘法 - 树形结构遍历:比如计算 DOM 节点子树高度,用节点引用作缓存键(Map 支持对象键),避免反复遍历同一子树
注意边界与清理机制
缓存不是越多越好,得配合业务节奏控制生命周期:
- 长期运行的页面中,无限增长的缓存会吃内存——加 LRU 限制(如最多存 100 项)或 TTL 过期(如 5 分钟后自动失效)
- 提供
.clear()方法,方便在数据源刷新、用户登出等时机手动清空 - 避免缓存副作用函数(如含
Date.now()、Math.random()或 DOM 查询),否则结果不可复现,缓存反而出错
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











