ES6 函数尾调用优化

作用

  • 尾调用优化不再创建新的栈帧,而是清除并重用当前栈帧,所以可以帮助函数保持更小的调用栈,减少内存的使用,避免栈溢出错误。
    如下递归函数,如果没有尾调用优化,持续递归一段时间后,由于递归调用次数多,会导致调用栈溢出,引发错误。进行优化后,调用栈中只会存在一个栈帧,避免栈溢出错误。
function func() {
    // do something
    return func();
}

前提条件

  1. 尾调用不访问当前栈帧的变量
  2. 在函数内部,尾调用是最后一条语句
  3. 尾调用的结果作为函数值返回
  • 不满足条件一:
function  f1() {
    var num = 1,
          f2 = () => num;
    return f2();
}
// 由于 f2 使用了 f1 中的 num 变量,即使是调用后立即返回结果也无法进行尾调用优化
  • 不满足条件二
function func() {
    var result = func();
    return result;
}
// 由于调用不在尾部,无法进行优化
  • 不满足条件三
function func() {
    return 1 + func();
}
// 由于在尾调用返回后还进行了其他操作 (1 + ),函数无法得到优化

使用场景

  • 递归函数是最主要的应用场景,效果最为显著

️以阶乘函数举例

function factorial(n) {
    if (n <= 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

当传入 n 值过大时,会导致调用栈尺寸不断增长,最终导致栈溢出的潜在风险。

现在我们对这个函数进行优化,使其满足尾调用优化的条件

function factorial(n, p = 1) {
    if (n <= 1) {
        return 1 * p;
    } else {
        let result = n * p
        return factorial(n - 1, result);
    }
}

由于尾调用优化,所以无论传入的 n 值为多少,尾调用都不会创建新的栈帧,而是重复使用当前栈帧,所以不会带来栈溢出的风险。

总结

在进行编写递归函数时,利用尾调用优化的特性优化递归函数,将会提升程序的性能。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容