尾调用,尾调用优化,尾递归,尾递归优化实现

本文讨论了尾调用的概念,其如何通过避免保留外层函数调用帧来节省内存。尾递归的利用和尾调用优化在ES6中的角色,以及严格模式对尾调用的影响。通过实例展示了如何通过循环实现尾递归优化,以防止栈溢出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

尾调用

函数的最后一步是调用其他函数,只要是函数的最后一步操作是调用其他函数都可以算是尾调用。

function f(x){
   if(x > 0){
        return m(x)    
    }else {
    return n(x)
}
}
// 上面的m(x),n(x)都属于尾调用

由于函数的调用会形成调用栈,若A函数内部还有B函数执行,则在A函数的调用帧上面会有B函数的调用帧,依次类推,就形成了调用栈。

尾调用优化

会大大节省内存,由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。只有不再用到外层函数的内部变量,内部函数的调用帧才会取代外层函数调用帧。

function (){
    let num = 1
    function fn(a){
        return a + num
    }
    return fn(1)
}
// 不属于尾调用优化,由于fn函数使用了外部函数的变量num

尾递归

尾调用自身,就称为尾递归。由于递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

// 普通递归
function fibonacci(n){
    if(n <= 1) return 1
    return fibonacci(n - 1) + fibonacci(n - 2)
}
console.time()
fibonacci(10)   //89  default: 0.03515625 ms
fibonacci(100)  //超时
console.timeEnd()  

//尾递归
function fibonacci2(n,val = 1,val2 = 1){
    if(n <= 1) return val2
    // 使用es6的默认赋值方式,将值作为函数的参数,这样就没有使用外部函数内的变量,从而实现取代外部函数调用帧
    return fibonacci2(n - 1, val2, val + val2)
}

console.time()
fibonacci2(10)   //89  default: 0.03515625 ms
fibonacci2(100)  //573147844013817200000 default: 0.0478515625 ms
console.timeEnd()  

严格模式

ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。

  • func.arguments:返回调用时函数的参数。
  • func.caller:返回调用当前函数的那个函数。

尾调用优化发生时,函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效

尾递归优化实现

尾递归之所以需要优化,原因是调用栈太多,造成溢出,那么只要减少调用栈,就不会溢出。怎么做可以减少调用栈呢?就是采用“循环”换掉“递归”

function tco(fn){
    let value; 
    let active = false  //是否进入尾递归
    let arr = []
    return function accumulator(){
    arr.push(arguments) //保存每一轮递归执行的参数,所以循环总是会执行的
    if(!active){
    avtive = true
    while(arr.length){  //此处采用循环
    value = fn.apply(this,arr.shift())
}
    active = false
    return value
}
}
}

var sum = tco(function(x,y){
    if(y>0){
    return sum(x+1,y-1) //每次递归返回的undefined,所以避免了递归执行
}else {
    return x
}
})
sum(1,1000) // 100001

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值