一、什么是尾调用
尾调用就是在函数执行的最后一步调用另一个函数。
如下所示为尾调用:
function f(x) {
return g(x)
}
function b(num) {
return c(num + 2)
}
下面的两种情况都不属于尾调用:
function f(x){
let y = g(x)
return y
}
function f(x){
return g(x) + 1
}
二、为什么说尾调用的性能要比没有使用尾调用的性能好呢?
代码执行是基于执行栈的,所以当在一个函数里调用另一个函数时,会保留当前的执行上
下文,然后再新建另外一个执行上下文加入栈中,这样比较占用内存;
而使用尾调用的话,因为已经是函数的最后一步,执行尾调用函数时,就可以不必再保留当前执行的上下文,从而节省了内存,这就是尾调用优化。
但是 ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
三、尾递归
函数调用自身,叫做递归。如果尾调用自身,则就称为尾递归。
递归非常消耗内存,因为需要同时保存成千上万个调用记录,很容易发生栈溢出的错误。
但是对于尾递归来说,由于只存在一个调用记录,所以永远不会发生栈溢出。
- 求阶乘的递归 ---- 容易造成内存的泄露
function factorial(n) {
if (n === 1) return 1
return n * factorial(n - 1)
}
console.log(factorial(5)) //120
- 求阶乘的递归,优化成尾递归,
function factorial(n, total) {
if (n === 1) return total
return factorial(n - 1, total * n)
}
console.log(factorial(5, 1))
如上述代码所示,我们使用尾递归,fatorial(5, 1)的值也就是factorial(4, 5)的值,同时也是factorial(3, 20)…这样调用栈中,每一次都只有一个函数,不会导致内存泄露。
四、严格模式
ES6中的尾调用优化只在严格模式下开启,正常模式下无效。
这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。严格模式下禁用这两个变量,所以尾调用模式仅在严格模式下生效。
参考地址:https://blog.csdn.net/weixin_47450807/article/details/123396329