JavaScript递归阶乘需基础情况(n===0返回1)防止栈溢出,但重复计算低效;加Memoization缓存结果可提速,手动实现用函数属性cache,通用方案用memoize高阶函数配合Map存储。

JavaScript中用递归计算阶乘很简单,但默认实现会重复运算;加上Memoization就能显著提速,核心是“算过一次就记下来,下次直接用”。
递归实现阶乘的基本写法
阶乘的数学定义天然适合递归:n! = n × (n−1)!,且 0! = 1。对应到代码,就是判断基础情况(n为0或1),再调用自身处理更小的数。
- 基础情况必须明确——通常用 n === 0 返回 1,这是递归终止的关键,缺了就会无限调用导致栈溢出
- 递归调用要确保参数不断靠近基础情况,比如每次传入 n - 1
- 典型代码:
if (n === 0) return 1;
return n * factorial(n - 1);
}
例如 factorial(4) 会依次展开为 4 × factorial(3) → 4 × 3 × factorial(2) → … → 24,整个过程在调用栈中逐层压入、再逐层返回。
为什么需要Memoization优化
原始递归在多次调用时效率低:比如先算 factorial(5),再算 factorial(6),后者会重新计算一遍 factorial(5) 及更小的值,造成冗余。尤其当函数被频繁调用或参数范围较大时,重复计算量快速上升。
立即学习“Java免费学习笔记(深入)”;
- 这种重复源于“重叠子问题”——不同主调用可能请求相同输入
- Memoization本质是空间换时间:用一个对象(或Map)缓存 输入→输出 映射
- 每次执行前先查缓存,命中就跳过计算;未命中才执行并存结果
手动添加缓存的阶乘函数
不需要引入额外库,几行代码就能升级原有函数:
function factorial(n) {if (n === 0) return 1;
if (factorial.cache === undefined) factorial.cache = {};
if (n in factorial.cache) return factorial.cache[n];
factorial.cache[n] = n * factorial(n - 1);
return factorial.cache[n];
}
- cache作为函数属性,避免污染全局,也保证多次调用共享同一缓存
- 检查 n in factorial.cache 比 factorial.cache[n] !== undefined 更稳妥,能区分 0 和 undefined
- 首次调用 factorial(6) 会顺带把 0~6 的结果全存进 cache,后续调用 factorial(4) 或 factorial(5) 都直接返回
通用memoize高阶函数封装
如果多个递归函数都需要优化,可以抽象出一个可复用的 memoize 工具:
function memoize(fn) {const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const memoFactorial = memoize(function(n) {
if (n === 0) return 1;
return n * memoFactorial(n - 1);
});
- 使用 Map 而不是普通对象,支持任意类型参数(如数组、对象)作键
- JSON.stringify(args) 是简单键生成方式,适用于参数为基本类型或纯对象的场景
- 这样封装后,memoFactorial 就自带记忆能力,无需修改原逻辑


















