You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归函数如何访问本地memo对象存储的信息?以记忆化斐波那契为例

带记忆化递归斐波那契的memo作用域问题解答

你正在分析的记忆化斐波那契实现代码如下:

function fib(n, memo = {}) {
    if (n in memo) return memo[n];
    if (n === 1 || n === 2) return 1;

    memo[n] = fib(n - 1, memo) + fib(n - 2, memo); 
    return memo[n]
}

问题核心:混淆值传递和引用传递规则

你的认知偏差本质是忽略了JavaScript中对象类型按引用传递的特性,没有注意到代码里memo参数的传递逻辑:

  • 首先,memo = {}这个默认参数赋值,仅在最外层第一次调用fib且未传入第二个参数时才会执行。也就是说整个递归链路的起点,只会创建一个空对象作为缓存容器。
  • 后续所有递归调用fib(n-1, memo)、fib(n-2, memo)时,都是显式把当前作用域持有的memo作为实参传入下一层函数,并没有让下一层函数新建独立的memo对象。

JavaScript的参数传递规则:
原始类型(数字、字符串、布尔、null、undefined、Symbol)按值传递,函数形参拿到的是值的独立副本,修改形参不会影响外部变量;
对象、数组、函数这类引用类型,形参拿到的是指向原对象内存地址的引用,不是对象的独立拷贝,通过引用修改对象内容时,所有持有该对象引用的作用域都会看到变更。

我们可以用一个极简例子验证这个逻辑:

function setValue(obj) {
  obj.test = 123;
}
const outerObj = {};
setValue(outerObj);
console.log(outerObj.test); // 输出123,外层原对象被直接修改

对应到斐波那契递归的完整执行逻辑

以调用fib(5)为例,整个memo的流转过程是:

  1. 最外层调用fib(5),未传第二个参数,初始化memo为指向堆内存中空对象的引用。
  2. 计算memo[5]需要调用fib(4, memo),此时把memo存储的内存地址传给fib(4)的形参,fib(4)作用域内的memo和fib(5)的memo指向堆中同一个对象。
  3. fib(4)计算时继续调用fib(3, memo)、fib(2, memo),所有递归层级的memo形参,都指向最外层创建的那同一个缓存对象。
  4. 任意一层递归执行memo[n] = 计算结果时,修改的都是堆内存中同一个对象的属性,自然所有上层作用域持有的memo都能同步读到写入的缓存值。

原有认知的纠正

你之前认为“嵌套调用时被调用函数无法直接访问调用方局部变量”这个结论本身是正确的,但这个场景里根本不存在“跨作用域偷偷访问外部变量”的情况:是代码主动通过参数,把同一个缓存对象的引用一层层传递给了所有递归调用,大家操作的本来就是同一个对象,自然会同步更新。
如果修改代码,递归调用时不传入memo,写成memo[n] = fib(n-1) + fib(n-2),那每一层调用都会新建一个独立的空memo对象,记忆化会完全失效,这时候才符合你之前“各次函数调用完全独立”的认知。

内容的提问来源于stack exchange,提问作者DC1477

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 08:51:28