JS调用栈

调用栈 JS执行时会形成调用栈,调用一个函数时,返回地址、参数、本地变量都会被推入栈中,如果当前正在运行的函数a调用函数b,则函数b相关内容也会被推入栈顶。函数a执行完毕后,与a相关的内容都会被弹出调用栈。由于复杂类型值存放于堆中,因此弹出的只是指针,他们的值依然在堆中,由GC决定回收。

换而言之,函数调用会在内存中形成一个“调用记录”,又称“调用帧”,保存调用位置和内部变量等信息。如果在函数a的内部调用函数b,那么在a的调用帧上方,还会形成一个b的调用帧。等到b运行结束,将结果返回到a,b的调用帧才会消失。如果函数b的内部还调用函数c,那就还有一个c的调用帧,以此类推。所有的调用帧就形成一个“调用栈”。

尾调用 尾调用是指某个函数的最后一步是调用另一个函数。

尾调用优化 看下面的例子:

function g (item){
    return item
}
function f(){
    let m = 1
    let n = 2
    return g(m + n)
}
f()
// 上面的例子实际等同于  g(3)
复制代码

上面的代码中,如果函数g不是函数f的尾调用,函数f就需要保存内部变量m和n的值、g的调用位置等信息。但是由于调用g之后,函数f就结束了,所以执行到最后一步,完全可以删除函数f的调用帧,只保留g(3)的调用帧。 这就叫“尾调用优化”,即只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这样大大节省内存,这就是“尾调用优化”的意义。 注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则无法进行“尾调用优化”。

function addOne(a){
    let one = 1
    function inner(b){
        return b + one
    }
    return inner(a)
}
复制代码

显而易见,上面的函数不会进行尾调用优化,因为内层函数inner用到了外层函数addOne的内部变量one。

尾递归 递归就是函数调用自身,如果尾调用的函数是函数自身,就称为尾递归。 递归非常消耗内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”,但是对于尾递归来说,只存在一个调用栈,就不会发生“栈溢出”错误

转载于:https://juejin.im/post/5cb67ffaf265da03b11f2e4a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值