尾调用优化

325 阅读2分钟

尾调用优化的意义

ES6 规范新增了一项内存管理优化机制,让JavaScript引擎在满足条件时可以重用栈帧。具体来说,这项优化非常适合“尾调用”,即外部函数的返回值是内部函数的返回值,比如:

function outerFunction() {
    return innnerFunction()
}

在ES6优化之前,执行这个例子会在内存中发生如下操作:

(1)执行到 outerFunction函数体,第一个栈帧被推到栈上。

(2)执行 outerFunction函数体,到 return 语句。计算返回值必须先计算 innerFunction

(3)执行到innerFunction函数体,第二个栈帧被推到栈上。

(4)执行innerFunction函数体,计算其返回值。

(5)将栈帧弹出栈外。

在ES6优化之后,执行这个例子内存发生的变化如下:

(1)执行到 outerFunction函数体,第一个栈帧被推到栈上。

(2)执行 outerFunction函数体,到 return 语句。为求值返回值,必须先计算 innerFunction

(3)引擎返现把第一个栈帧弹出栈外也没有问题,因为innerFunction的返回值也是outerFunction的返回值。

(4)弹出outerFunction的栈帧。

(5)执行到innerFunction函数体,栈帧被推到栈上。

(6)执行innerFunction函数体,计算其返回值。

(7)将innerFunction的栈帧弹出栈外。

总结: 很明显,第一种情况下每多调用一次嵌套循环函数,就会增加一个栈帧。第二种情况下无论调用多少次嵌套函数,都只有一个栈帧。这就是ES6尾调用优化的关键:如果函数的逻辑允许基于尾调用将其销毁,则引擎就会这么做

尾调用优化的条件

(1)代码在严格模式下执行;

(2)外部函数的返回值是对尾调用函数的调用;

(3)尾调用函数返回后不需要执行额外的逻辑;

(4)尾调用函数不是引用外部函数作用中自由变量的闭包;

之所以要求严格模式,主要因为在非严格模式下函数调用中允许使用f.argumentsf.caller,而他们都会引用外部函数的栈帧,因此不能应用ES6中的优化。