每一个前端,在学递归时,都被斐波那契“温柔地折磨”过。
今天,我们不只写代码,而是彻底看懂:
- 为什么最经典的递归斐波那契会慢到怀疑人生?
- 什么是“用空间换时间”的真正含义?
- 为什么说 闭包 + IIFE 是 JavaScript 里实现记忆函数的“王炸组合”?
这篇文章,适合:
- 刚学递归、总被复杂度吓到的你
- 面试前想把“记忆化”真正讲清楚的你
- 想写出更“有 JS 味道”代码的你
一、斐波那契:看起来很简单,实际上很危险
先从最经典的写法开始:
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
fib(10); // 55
这段代码有三个优点:
- ✅ 数学公式一一对应
- ✅ 逻辑直观、非常优雅
- ✅ 递归思想体现得淋漓尽致
但它也有一个致命问题:
时间复杂度是 O(2^n)
当你执行 fib(40) 时,电脑并不是“算慢了”,而是在:
- 一遍遍重复计算相同的子问题
- 疯狂创建函数调用栈
- 在指数级的调用树里迷路
二、递归为什么会“爆炸”?
我们以 fib(5) 为例:
你会发现:
fib(3)被算了 2 次fib(2)被算了 3 次fib(1)被算了 2 次
问题不在递归,而在:
递归 + 重复子问题 = 性能灾难
三、第一层优化:用空间换时间
既然问题是“重复计算”,那思路就很清晰了:
算过的结果,存起来。
const cache = {};
function fib(n) {
if (n in cache) return cache[n];
if (n <= 1) {
cache[n] = n;
return n;
}
const result = fib(n - 1) + fib(n - 2);
cache[n] = result;
return result;
}
fib(100);
🎉 奇迹发生了:
- 时间复杂度 ➜ O(n)
- 调用次数大幅减少
- 性能直接起飞
但这里有个问题:
cache 是全局变量
在真实项目中,这通常意味着:
- 可能被意外修改
- 不利于封装
- 不够“优雅”
于是,我们需要下一步进化。
四、记忆函数的终极形态:闭包
如果你学过闭包,一定听过一句话:
闭包 = 函数 + 它能访问的自由变量
那我们能不能让 cache:
- 不暴露在全局
- 又能被多次调用记住
答案是:可以,而且非常 JS。
五、IIFE:只执行一次的“外壳”
先引入一个工具:
IIFE(Immediately Invoked Function Expression)
(function () {
// 定义后立刻执行
})();
它的特点非常适合做三件事:
- ✅ 创建私有作用域
- ✅ 初始化一次性逻辑
- ✅ 生成闭包
六、闭包 + IIFE:记忆型斐波那契完整版
const fib = (function () {
const cache = {};
return function (n) {
if (n in cache) return cache[n];
if (n <= 1) {
cache[n] = n;
return n;
}
cache[n] = fib(n - 1) + fib(n - 2);
return cache[n];
};
})();
fib(100);
我们来逐层拆解它。
七、这段代码“牛”在哪?
1️⃣ cache 被闭包保护
cache不在全局- 外界无法直接访问或修改
- 但每次调用
fib都能共享它
这正是闭包的精髓:
函数执行完了,但它的作用域没有消失
2️⃣ IIFE 只执行一次
const fib = (function () {
console.log('只执行一次');
return function () {};
})();
- 初始化逻辑只跑一次
- cache 只创建一次
- 后续全是高效复用
3️⃣ 递归依然存在,但不再“浪费”
递归没有错,错的是:
- 不加控制的重复递归
记忆化让递归从:
指数级灾难 ➜ 线性可控
八、这其实是一个通用模式
你现在学到的,不只是斐波那契。
这是一个通用的记忆函数模板:
function memo(fn) {
const cache = {};
return function (key) {
if (key in cache) return cache[key];
return cache[key] = fn(key);
};
}
很多库、很多面试题,本质都在考这个思想。
九、什么时候该用这种写法?
非常适合:
- 有大量重复子问题
- 输入 ➜ 输出稳定
- 纯函数或近似纯函数
例如:
- 递归算法
- 复杂计算缓存
- 前端性能优化
十、一句话总结
递归决定思路,缓存决定性能,闭包决定优雅。
如果你能把“斐波那契 + 闭包 + IIFE”讲清楚:
- 你已经超过了大多数只会背概念的候选人
- 你也真正理解了 JavaScript 的执行模型
如果这篇文章对你有帮助,欢迎点赞、收藏、评论 👏