深入前端尾递归
创始人
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函数,这两个函数中巧妙利用循环解决了调用栈溢出的问题。

参考:尾递归优化的实现

相关内容

热门资讯

人的一生就是为了,传宗接代吗? 人的一生就是为了,传宗接代吗?那当然不是,每个人都有自己的价值观,如果你有这个想法,证明你被他们影响...
洛克王国诙谐神殿怎么打啊,我有... 洛克王国诙谐神殿怎么打啊,我有70的罗隐和51的少林咕咕,和45的音速,还有一只44的火神,这样能过...
手机屏幕上怎么弄出字 手机屏幕上怎么弄出字手机屏幕上怎么弄出字手机屏幕上怎么弄出字:长按手机桌面,然后点击窗口小工具或小组...
跪求花开伊吕波的结局是怎么样的... 跪求花开伊吕波的结局是怎么样的!真的是得了白血病吗?是的话!KIR了编剧楼主哪里得到的消息? 花开...
苏格兰民歌 一路平安 苏格兰民歌 一路平安还没听过,不好意思,帮不了你
谁能帮我起一个好听的英文名(要... 谁能帮我起一个好听的英文名(要中文)男女?女 我比较喜欢--joy(乔伊)、Renee(瑞妮)、Ma...
寻高手对下联~ 寻高手对下联~琵琶琴瑟八大王 王王在上魑魅魍魉四小鬼,鬼鬼靠边!魑魅魍魉四小鬼,鬼鬼靠边魑魅魍魉四...
《一位母亲与家长会》的3道阅读... 《一位母亲与家长会》的3道阅读题1.我没有原文。对不起。2.因为母亲在鼓励她的孩子,她要使自己的孩子...
深深的喜欢等于爱吗? 深深的喜欢等于爱吗?喜欢不等于爱。但当你站在你喜欢的人面前,你只感到开心但当你与你喜欢的人四目交投,...
《人性的弱点》一书作者是谁? 《人性的弱点》一书作者是谁?《人性的弱点》·作者:(美)戴尔·卡耐基文名:DaleCarnegie戴...
浪漫一生的英文怎么写 浪漫一生的英文怎么写a so long liferomance all one's lifeRoma...
古代什么词可以指代美女? 古代什么词可以指代美女?谢谢!一楼的你说的不对吧?我说的是指代,不是形容啊。形容我也会,倾国倾城,冰...
以前有看过部分小说是《神雕侠侣... 以前有看过部分小说是《神雕侠侣》后面的,不知是那部小说? 想问问?不是,是别人续写的一部小说?只是记...
在工作中学习到什么?” 在工作中学习到什么?”工作中可以学到与本职工作相关的技术、技巧。了解工作的流程。以及本工作的重点及注...
《西游记》中女妖怪有不少,其中... 《西游记》中女妖怪有不少,其中最可怜的女妖怪是谁?中女妖怪有不少,其中最可怜的女妖怪是白骨夫人最可怜...
英文名字‘爱丽儿’的英文到底怎... 英文名字‘爱丽儿’的英文到底怎么写?!Alier Ariel作为英文名字,这两个里哪个更好?先谢谢...
武术在实战中有用吗 武术在实战中有用吗真的打架能不能那么帅?有用,一个会武术的人和一个会武术的人格斗,就要使用一些较为复...
关于模拟人生3夜店人生 关于模拟人生3夜店人生1你的Net Framework版本过低,去安装 一个最新版的4.0的2这个问...
吴启华版倚天屠龙记张无忌哪一集... 吴启华版倚天屠龙记张无忌哪一集上的武当山吴启华版倚天屠龙记张无忌哪一集上的武当山,就是扮成个小道童,...
《三国演义》战长沙的时候,如果... 《三国演义》战长沙的时候,如果关羽的拖刀计用全了,能够斩杀黄忠吗?我认为是可以斩杀黄忠的,因为当时黄...