深入前端尾递归
创始人
2024-05-26 06:46:23
0

在深入探讨前端尾递归前,我们先来了解递归和尾调用两个概念

递归

在函数内部调用自身,一般来说递归有两个状态

  • 递归状态(继续递归)
  • 最终状态(终止递归)

递归式方法可以被用于解决很多的计算机科学问题,因此它是计算机科学中十分重要的一个概念。绝大多数编程语言支持函数的自调用,在这些语言中函数可以通过调用自身来进行递归。计算理论可以证明递归的作用可以完全取代循环,因此在很多函数编程语言(如Scheme)中习惯用递归来实现循环。(递归算法)

在这里插入图片描述
一般而言,递归使函数看起来更加简洁,但相对循环来说不易理解。
在JavaScript语言中,函数中调用函数会形成调用栈(call stack),栈是一后进先出的结构,当函数调用链足够长时,会发生栈溢出,程序会报错,因此在使用递归时,要注意溢出的情况。

递归循环
程序简洁易阅读
栈溢出不会发生溢出

尾调用

在讨论尾调用前,我们先强调一点,尾调用的局限场景

  • safari浏览器实现了对尾调用的支持
  • 严格模式下生效

含义
函数最后一步是调用一个函数,最后一步并不指最后一行。可简单归纳为return + fn()

function bar(){}
function foo1() {bar()
}
// foo1不是尾调用,实际等价于
function foo1() {bar()return undefined
}// foo2不是未调用,因为最后一步不是调用一个函数,而是 bar() + 1 求和
function foo2() {return bar() + 1
}// 是尾调用
function foo3(num) {switch(num) {case 1:return bar()case 2: return bar()}
}

尾调用由于最后一步是调用一个函数,所以外部函数的调用位置,内部变量不需要用到,因此不要保存外部函数的调用帧,因此执行到尾调用函数的最后一步时,内存函数的调用帧取代最外层函数的调用帧。不会发生调用栈溢出。

尾递归

尾递归,递归结合尾调用,或者说尾调用的函数是自身。

尾递归优化

前提条件

  • 严格模式
  • Safari浏览器

尾递归溢出

function sum(x, y) {if (y > 0) {return sum(x + 1, y - 1);} else {return x;}
}sum(1, 100000);

在这里插入图片描述

尾调用优化

蹦床函数

封装一个蹦床函数解决调用栈溢出

   function trampoline(f) {while(typeof f == 'function') {f = f()}return f}

trampoline函数接收一个函数,将递归改造成循环,我们将sum函数进行改造,返回sum函数的一个副本。

function sum(x, y) {if (y > 0) {return sum.bind(null,x + 1, y - 1);   // 这里可以不使用bind,直接返回一个函数,函数内部调用sum方法} else {return x;}
}
// 不使用bindtrampoline(sum(1, 100000))   // 100001

为了解决调用栈溢出的问题,我们封装了一个trampoline函数,另外对sum函数进行了改造。

tco函数

 function tco(f) {let value,actived = false,accumulated = [];return function () {accumulated.push(arguments);if (!actived) {actived = true;while (accumulated.length > 0) {value = f.apply(null, accumulated.shift());}actived = false;return value;}};}var sum = tco(function (x, y) {if (y > 0) {return sum(x + 1, y - 1);} else {return x;}});

巧妙利用accumulated,循环不会终止。每次执行到apply时,accumulated中会push进下一次参数,继续while循环。tco函数也使用了闭包。

总结

本文主要讨论了以下几个问题
1)前端中的尾递归,介绍了递归,尾调用,和尾递归三个基本概念。
2) 说明了尾调用的优化需要在严格模式下才会生效,尾调用优化在safari浏览器中得到实现。
3)递归可能造成调用栈溢出
4)可以使用循环解决调用栈溢出问题
5)封装了trampoline函数和tco函数,这两个函数中巧妙利用循环解决了调用栈溢出的问题。

参考:尾递归优化的实现

相关内容

热门资讯

【华西宏观】7月理财或迎万亿增... (转自:华西研究)华西宏观(一)本轮理财季末回表压力或弱于季节性规律本周(6月23-27日)理财回表...
新潮运动有望进一步落地生根(链...   李耀章  近年来,跑酷、街舞、攀岩、滑板等新潮运动受到儿童青少年欢迎。从城市街头到校园课堂,从兴...
央视聚焦!福州这里助力闽江绿色... 转自:沧州发布当1993年立秋后的第一只白鹭掠过水口水电站时涡轮机组的旋转赋予了这条万年奔流不息的江...
NENS2025产业论坛在北京... 转自:科技日报科技日报记者 华凌6月28日,第七届纳米能源与纳米系统国际会议(NENS2025)产业...
宁波韵升5000吨稀土磁材产能... 长江商报消息 ●长江商报记者 潘瑞冬钕铁硼永磁材料公司宁波韵升(600366.SH)新产能落地。近日...
和林微纳拟赴港IPO推进全球化... 长江商报消息 ●长江商报记者 潘瑞冬微型精密电子零部件公司和林微纳(688661.SH)推进“A+H...
国际金融市场早知道:7月1日 转自:新华财经【资讯导读】•特朗普“大而美”法案通关遇阻 机构推演美债规模扩大•白宫官员说美国将恢复...
普陀区举办第六届政务服务“网办... 转自:上观新闻6月30日,普陀区以“智享好办 数聚惠达”为主题,举办第六届政务服务“网办季”启动仪式...
今天,你的账户或将多一笔钱 (转自:南昌新观察)7月1日,你将有一笔额外收入 一年一度的公积金存息 结息后利息将直接打入公积金账...
困难家庭大学新生 可获一次性救...   本报讯(记者 李玥)6月30日,哈尔滨市民政局启动2025年困难家庭大学生一次性救助工作。我市城...