在ES5之前,考虑以下代码
function fac() {
let a;
return dosomething();
}
参照以上代码,尾调用的意义是指,函数作为某个函数的最后一个语句进行调用。ES5以前会像其他语言(C++,java)一样,会为dosomething
函数分配一个新的栈帧,调用完成后,再返回fac
函数,最后输出结果。
关于函数如何分配栈,以及保存断点等过程的知识,墙裂推荐神书CSAPP
这样新分配的栈帧就会带来额外的开销,严重影响程序的性能。
ES6的尾调用优化就不会新分配栈,而是会用dosomething
的函数栈,但满足尾调用优化要满足三个条件
- 尾调用不能访问当前栈帧中的变量(
dosomething
不能访问a,即不是一个闭包) - 尾调用必须是最后一条语句
- 尾调用必须作为结果返回
第一点:不能是闭包,下面代码就不行
function fac() {
let a = 1;
dosomething = ()=> a;
return dosomething();
}
第二点:像下面代码这样,ES6的引擎就不能进行尾调用优化
function fac() {
dosomething();
return 1;
}
第三点:这样也无法进行尾调用优化,因为最后返回还涉及到+运算,dosomething并不是最终的返回结果
function fac() {
return 1 + dosomething();
}
尾调用优化的应用
考虑求
n
!
n!
n!的递归函数
function fac(n) {
return n === 1 ? 1 : n * fac(n - 1);
}
可以这样使用尾调用优化,如果是计算量大的函数,则会大量优化程序性能
function fac(n, res = 1) {
if(n === 1) return 1;
return fac(n - 1, n * res);
}