跳到主要内容

尾调用

2024年03月12日
柏拉文
越努力,越幸运

一、认识


尾调用Tail Call 指某个函数的最后一步是调用另一个函数。

尾调用函数不会在调用栈上增加新的堆栈帧(不会增加调用栈的长度),而是直接更新调用栈,调用栈所占空间始终是常量,节省了内存,避免了爆栈的可能性

二、语法


2.1 直接尾调用

function foo1(){
return foo2(); // foo1 函数的最后一步调用另一个函数
}
function foo2(){
console.log("foo2 函数")
}

2.2 三元尾调用

const a = x => x ? f() : g()

// 改写成:

const a = x => {
if (x) {
return f()
} else {
return g()
}
}

可见 fg 的返回值都是直接被返回的,符合尾调用的定义。

2.3 逻辑尾调用

const a = () => f() || g()

// 改写成:

const a = () => {
const result = f()
if (result) {
return result
} else {
return g()
}
}

|| 运算符的结果依赖于 f 函数的返回值,而不是直接返回 f 的返回值,直接返回的只有 g 函数的返回值。说明, f 函数也不在尾递归位置上,而 g 函数在尾递归位置上

2.4 逗号尾调用

const a = () => (f(), g()) 

// 改写成:

const a = () => {
f()
return g()
}

可见,在尾递归位置上的仍然只有一个 g 函数。

2.5 非尾调用语法

  1. 单独的函数调用不是尾调用

    function foo() {  bar()}

    // 改写成:

    function foo() {
    bar()
    return undefined
    }

    可以看到 return 语句返回的只是一个 undefined 而并非 bar 函数的返回值,所以这里不存在尾调用。

  2. 外部函数的最后一步是调用内部的函数并返回

    function foo(){
    function bar(){

    }
    return bar();
    }

    函数返回一个内部函数并调用, 不是一个尾调用。

三、场景