大家好,我是小米,一个喜欢研究技术、也喜欢把复杂知识讲成故事的程序员大哥哥,今天想和大家聊一个 JavaScript 函数里的“小秘密”——尾调用优化(Tail Call Optimization,TCO)。
说到函数调用,很多小伙伴第一反应可能是:“函数调用有什么特别的?执行完返回结果不就结束了吗?”,其实没有那么简单,函数每调用一次,JavaScript 引擎都会给它准备一个“小房间”,这个房间在计算机里叫做调用栈(Call Stack)。
普通函数调用,就像你去餐厅吃饭:
- 服务员记录你的订单;
- 厨房开始准备;
- 你坐在那里等待;
- 吃完之后离开。
如果你一次只点一桌菜,没有问题,但是,如果你不停递归调用:
- “再来一桌!”
- “再来一桌!”
- “再来一桌!”
- ……
你的桌子越来越多,最后餐厅可能坐不下了,在程序里,这就是经典的栈溢出(Stack Overflow)问题,那么有没有一种方式,可以让函数调用的时候不用一直占用新的“小房间”呢?
答案就是尾调用优化。
什么是尾调用优化?
故事开始之前,我们先认识一下主人公,假设有一家快递公司,老板每天安排任务,普通函数调用就像:
- 老板:“小王,你先去仓库拿货,然后回来告诉我结果,我再安排下一步。”
- 小王:“好的。”
于是老板等待小王,小王完成任务后回来,老板继续执行,这里老板必须一直等待小王,所以老板的位置不能释放。
对应代码:
执行过程:
为什么 task1 不能结束?因为 return result+10;,这里还有计算,task2 返回以后,task1 还要继续工作。
但是,如果老板换一种安排:
- “小王,你直接把结果交给客户,不需要回来告诉我。”
- 小王:“收到。”
老板马上释放自己的位置,这就是尾调用。
代码: