尾调用优化(Tail Call Optimization)

尾调用优化可以避免栈溢出,但并非所有引擎都实现。


📚 基本概念

什么是尾调用

尾调用是函数最后一步调用另一个函数:

// ✅ 尾调用
function f(x) {
  return g(x); // 最后一步调用
}
 
// ❌ 不是尾调用
function f(x) {
  return g(x) + 1; // 调用后还有操作
}

🎯 尾递归

普通递归

// ❌ 普通递归:可能栈溢出
function factorial(n) {
  if (n <= 1) return 1;
  return n * factorial(n - 1); // 不是尾调用
}
 
factorial(10000); // 可能栈溢出

尾递归

// ✅ 尾递归:可以优化
function factorial(n, acc = 1) {
  if (n <= 1) return acc;
  return factorial(n - 1, n * acc); // 尾调用
}
 
factorial(10000); // 理论上可以优化

⚠️ 浏览器支持

当前状态

  • Safari:支持尾调用优化
  • Chrome/Edge:不支持
  • Firefox:不支持

检查支持

function testTailCall() {
  'use strict';
  function f(n) {
    if (n === 0) return true;
    return f(n - 1);
  }
  try {
    f(100000);
    return true; // 支持尾调用优化
  } catch (e) {
    return false; // 不支持
  }
}

💡 替代方案

1. 使用循环

// 将递归改为循环
function factorial(n) {
  let result = 1;
  for (let i = 2; i <= n; i++) {
    result *= i;
  }
  return result;
}

2. 使用蹦床函数(Trampoline)

function trampoline(fn) {
  while (typeof fn === 'function') {
    fn = fn();
  }
  return fn;
}
 
function factorial(n, acc = 1) {
  if (n <= 1) return acc;
  return () => factorial(n - 1, n * acc);
}
 
const result = trampoline(() => factorial(10000));

🔗 相关链接


参考


javascript 性能优化 尾调用 尾递归 tco