资讯专栏INFORMATION COLUMN

ES6被你忽略的尾调用

xeblog / 759人阅读

摘要:被你忽略的尾调用尾调用是什么在有一个新特性尾调用用最简单的一句话描述就是某个函数的最后一步再调用另一个函数,听起来挺简单的,但是它的功能特别强大,直接给你撸个例子吧。如果函数内部还调用函数,那就还有一个的调用记录栈,以此类推。

title: 被你忽略的‘尾调用’
date: 2017-05-02 16:52:22

tags: [ES6,javascript] 尾调用是什么?

在ES6有一个新特性:尾调用
用最简单的一句话描述就是‘某个函数的最后一步再调用另一个函数’,听起来挺简单的,
但是它的功能特别强大,直接给你撸个例子吧。

function a(x) {
  return b(x);
}

这个函数的最后一步再调用另一个函数,这就是尾调用。
以下几点不属于尾调用

在调用函数b之后还存在赋值的操作

function a(x) {
    let m = b(x);
    return m;
}

返回的那个函数没有加return

function a(x) {
    b(x);
}

在调用之后还存在其他的赋值操作

function a(x) {
    return b(x) - 2;
}
尾调用优化

函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置
和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用记录上方,还会形成
一个B的调用记录。等到B运行结束,将结果返回到A,B的调用记录才会消失。如果函数B
内部还调用函数C,那就还有一个C的调用记录栈,以此类推。所有的调用记录,就形成一个
"调用栈"

来个例子吧

function a() {
  let p = 2;
  let q = 3;
  return b(p + q)
}
a();

仔细观察上面的代码就会发现啊a函数似乎是多余的吧,因为b函数是尾调用函数,执行到
这里函数a早就结束了,完全可以删除a函数了只保留b的调用帧即可。
尾调用优化:就是只保留内层函数的调用帧,如果所有函数都是尾调用,那么完全可以
做到每次执行时调用帧只能有一项,将大大节省内存,也就是尾调用的意义所在了。
注意的一点就是内层函数运用了外层函数的变量便不能进行尾调用优化了。记住哦!

尾递归

顾名思义,在一个尾调用中,如果函数最后的尾调用位置上是这个函数本身,则被称为尾递
归。递归很常用,但如果没写好的话也会非常消耗内存,导致爆栈。但是对于尾递归来说只
存在一个调用栈,便永远不会发生“栈溢出”错误。
就以求一个给出数的阶乘来探索吧。在传统的做法中利用n的递减乘上原函数,这样复杂度
便会很高,数据量一大便会发生“栈溢出”的错误了。

传统的解决办法

function f(n) {
  if(n === 1) {
    return 1;
  }
  return n * f(n -1);
}

尾调用

function a(n, t) {
  if(n === 1) {
    return t;
  }
  return a(n - 1, n * t)
}

对比两个解决办法的复杂度就知道,尾调用只保留了一个记录。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91720.html

相关文章

  • try-catch-finally,被你忽略掉的执行顺序

    摘要:是捕捉异常的神器,不管是调试还是防止软件崩溃,都离不开它。今天笔者介绍一下加上后的执行顺序嗯按顺序执行了。现在笔者在语句块中故意报错看来,和的都需要先经过。 try-catch是捕捉异常的神器,不管是调试还是防止软件崩溃,都离不开它。今天笔者介绍一下加上finally后的执行顺序 function test() { try { console.log(1); } fin...

    bbbbbb 评论0 收藏0
  • try-catch-finally,被你忽略掉的执行顺序

    摘要:是捕捉异常的神器,不管是调试还是防止软件崩溃,都离不开它。今天笔者介绍一下加上后的执行顺序嗯按顺序执行了。现在笔者在语句块中故意报错看来,和的都需要先经过。 try-catch是捕捉异常的神器,不管是调试还是防止软件崩溃,都离不开它。今天笔者介绍一下加上finally后的执行顺序 function test() { try { console.log(1); } fin...

    浠ラ箍 评论0 收藏0
  • 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》

    摘要:每个函数调用都将开辟出一小块称为堆栈帧的内存。当第二个函数开始执行,堆栈帧增加到个。当这个函数调用结束后,它的帧会从堆栈中退出。保持堆栈帧跟踪函数调用的状态,并将其分派给下一个递归调用迭。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM...

    LeviDing 评论0 收藏0
  • js 实现斐波那契数列(数组缓存、动态规划、尾调用优化)

    摘要:根据该规则,返回第个斐波那契数。尾递归函数调用自身,称为递归。一个前端眼中的斐波那契数列解斐波那契数列的实用解法调用栈尾递归和手动优化尾调用优化译我从用写斐波那契生成器中学到的令人惊讶的件事 斐波那契数列是以下一系列数字: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, ... 在种子数字 0 和 1 ...

    赵连江 评论0 收藏0
  • ES6入门之函数的扩展

    摘要:如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。等同于等同于注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行尾调用优化。 showImg(https://segmentfault.com/img/bVbrTHp?w=1080&h=1920); 1. 函数参数的默认值 1.1 用法 在ES6之前是不能为...

    dackel 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<