什么是尾调用优化

函数的调用会在内存中形成一个调用记录,被称为调用帧,调用帧会保留调用位置和内部变量等信息,一个个调用帧形成了调用栈。

而尾部调用优化,意思是:如果一个函数的结尾调用了一个函数,且这个函数不需要依赖当前函数的调用帧,那么就会只保留尾部调用的这个函数调用帧,从而节省内存。

es6要求引擎实现尾部调用优化(TCO),因为如果没有TCO会导致一些JavaScript算法因为害怕调用栈限制而降低了通过递归实现的效率。

由于缺失TCO确实会导致一些程序无法实现,所以它变成了一个重要的语言特性而不是隐藏的实现细节。

es6确保了JavaScript开发者从现在开始可以在所有符合es6+的浏览器中依赖这个优化,这对JavaScript性能来说是一个胜利。

代码示例

function foo(x) {
    return x;
}

function bar(y) {
    return foo(y + 1); // 尾调用
}

function baz() {
    return 1 + bar(40); // 非尾调用,依赖了1
}

baz(); // 42

比较常见的还有递归:

function factorial(n) {
    if (n < 2) return 1;
    return n * factorial(n - 1);
}
factorial(5); // 120 

//尾调用优化
function factorial(n) {
    function fact(n, res) {
        if (n < 2) return res;
        return fact(n - 1, n * res);
    }
    return fact(n, 1);
}

factorial(5); // 120
需要注意:TCO只用于有实际尾调用的情况,如果你书写了一个没有尾调用的递归函数,那么性能还是会和以前一样,引擎对栈和帧的分配是有上限的,比如溢出报错,所以,如果你需要写一个递归,可以使用尾调用的方式,但是需要认真注意实现的细节。

TCO的存在对递归算法来说,引擎不再需要限制栈深度了。

分类: 你不知道的JavaScript 标签: 尾调用优化TCO

评论

暂无评论数据

暂无评论数据

目录