1、尾递归
现在就轮到本篇文章的主角——尾递归了,看一下下面这段简单的递归代码:
const sum = (n) => {
if (n <= 1) return n;
return n + sum(n-1)
}
就是计算从1到n的整数的和,显然这段代码并不是尾递归,因为sum(n-1)调用后还需要一步计算的过程,所以当n较大时就会导致栈溢出。我们可以把这段代码改为尾递归的形式:
const sum = (n, prevSum = 0) => {
if (n <= 1) return n + prevSum;
return sum(n-1, n + prevSum)
}
这样就是尾递归了,这段代码在 safari 里以严格模式运行时,不会出现栈溢出错误,因为它对尾调用做了优化。那有多少浏览器会做优化呢?其实在es6 的规范里,就已经定义了对尾调用的优化,不过目前浏览器对其支持情况很不好:
2、尾调用
当函数a的最后一个动作是调用函数b时,那么对函数b的调用形式就是尾调用。比如下面的代码里对fn1的调用就是尾调用:
const fn1 = (a) => {
let b = a + 1;
return b;
}
const fn2 = (x) => {
let y = x + 1;
return fn1(y); // line A
}
const result = fn2(1); // line B
我们知道,在代码执行时,会产生一个调用栈,调用某个函数时会将其压入栈,当它 return 后就会出栈,下图是对于这段代码简易示例的调用栈(没有对尾调用做优化)