为了账号安全,请及时绑定邮箱和手机立即绑定

Javascript尾调用优化

JavaScript 尾调用优化
递归
递归函数的形式通常是一个函数通过名称调用自己

比如经典的返回斐波那契数列生成问题

function fibonacci (n) {
if (n === 1 || n === 2) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
1
2
3
4
5
6
我们求斐波那契数列的第10项时,可以轻松求出 fibonacci(10) = 55, 可是当我们要求斐波那契数列的第1000项时, fibonacci(1000) 却没那么容易求出来甚至于报错

原因就是递归的每一次调用都会推一个栈帧到调用栈中,当调用层数越来越多,调用栈中的栈帧越来越多,将调用栈都给塞满了,也就出现了堆栈溢出的报错。

这个时候我们就需要对我们的代码进行优化了:

第一种方法就是将递归改写成循环
第二种就是将介绍的尾调用优化
尾调用优化
ECMAScript 6 规范新增了一项内存管理优化机制,让 JavaScript 引擎在满足条件时可以重用栈帧。

那么什么叫 “尾调用” 呢?

即外部函数的返回值是一个内部函数的返回值。

function outerFunc () {
return innerFunc();
}
1
2
3
当执行到 outerFunc 函数体时,第一个栈帧被推入栈,

到达 return innerFunc(); 语句时,发现要返回需要先求值,于是第二个栈帧也被推到栈上,

此时会发现其实第一个栈帧似乎没什么用了,因为 innerFunc() 的返回值也是 outerFunc() 的返回值

于是会将第一个栈帧弹出,而保留第二个,也就是尾调用优化。

尾调用优化的达成条件
条件
尾调用优化的条件就是确定外部栈帧真的没必要存在了

代码在严格模式下执行

外部函数的返回值是对尾调用函数的调用

尾调用函数返回后不需要执行额外的逻辑

尾调用函数不是引用外部函数作用域中自由变量的闭包

符合尾调用优化的例子
"use strict";

function outerFunction1 () {
return innerFunction();
}

function outerFunction2 (condition) {
return condition ? innerFunction1() : innerFunction2();
}
1
2
3
4
5
6
7
8
9
不符合尾调用优化的例子
"use strict";

// 不符合:尾调用并没有返回
function outerFunction1 () {
innerFunction();
}

// 不符合:尾调用并没有直接返回
function outerFunction2 () {
const res = innerFunction();
return res;
}

// 不符合:尾调用之后还有额外的逻辑操作(.toString())
function outerFunction3 () {
return innerFunction().toString();
}

// 不符合:尾调用是闭包
function outerFunction4 () {
let name = “ienyh”;
return function () { return name; };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
总结 & Last
尾调用优化在递归场景下的效果是最明显的,因为递归代码容易在栈内存中迅速产生大量栈帧。
————————————————
作者:ienyh
原文链接:https://blog.csdn.net/qq_45265059/article/details/118896280

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消