以前分享过递归,其中有一个优化就是尾调用。编程
先明确尾调用的概念:bash
尾调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是return调用另外一个函数。函数式编程
function fn() {
return gn()
}
复制代码
下面几种不是尾调用:函数
function fn() {
return gn() + 10//调用以后又赋值
}
function fn() {
gn()//没有return,或者说是return了undefined
}
function fn() {
var gnn = gn();
return gnn//调用后还有操做
}
复制代码
注意,是最后一步操做,不是放在最末尾,下面也算尾调用:优化
function fn(a) {
if(a > 10){
return gn(10)
}else{
return gn(20)
}
return gn()
}
复制代码
以前分享过调用栈,若是不是尾调用,那么会生成一个调用栈,直到栈顶的执行完毕,才会释放以前造成的调用栈的内存。尾调用由于是最后一步操做,因此不须要保留以前的栈,也就不须要保存以前的内存,就是递归里面计算阶乘那两个函数。ui
注意,并非全部的函数都能尾调用优化,要看你这个函数需不须要使用某些上个函数的变量或者什么的。spa
尾调用优化其实很大一部分就是递归函数在使用,由于递归函数调用的时候很是耗费内存,可能须要保存成百上千调用栈,很容易内存溢出。若是是尾递归就只有一个调用栈,能把复杂度O(n)的变成O(1)。code
至于怎么改写递归变成可使用尾调用就比较复杂了,须要根据不一样函数去修改。好比阮一峰大神的例子:cdn
function sum(x, y) {
if (y > 0) {
return sum(x + 1, y - 1);
} else {
return x;
}
}
sum(1, 100000)
复制代码
改为:递归
function sum(x, y) {
if (y > 0) {
return sum.bind(null, x + 1, y - 1);
} else {
return x;
}
}
复制代码
而后使用蹦床函数:
function trampoline(f) {
while (f && f instanceof Function) {
f = f();
}
return f;
}
复制代码
执行:
trampoline(sum(1, 100000))
复制代码
你会发现,不少递归函数都能改为相似的,而后使用蹦床函数实现尾调用优化。
而ES6对尾调用有什么优化?就是函数默认值,在一些场景下,好比阶乘的递归,采用默认值实现尾递归优化。