最近在看《冴羽的博客》,讲真,确实受益匪浅,已经看了javascript 深入系列和专题系列的大部分文章,可是现在才想起来做笔记。所以虽然很多以前面试被问得一脸懵逼的问题都被“一语惊醒梦中人”过,注意这里我说的是“过”。是的,这些知道点,当时看的时候跟着大佬的思维,确实当时感觉“哦~ 原来是这样”,但是,看了下篇把上篇的知识忘了还是让我感觉自己太挫了。
于是,决定写点笔记来加深一点印象吧!
  今天看到了“javascript专题之递归” (https://github.com/mqyqingfeng/Blog/issues/49) 这篇,所以就从这里开始吧!
在这里我再一次看到了尾调用和尾递归这俩个的关键字,距离前一次看到好像还是很多年前的事了,但是当时完全不明所以啊...,但是,今天,结合了之前的执行上下文执行上下文栈的知识,总算明白这到底是个啥了!
  尾调用,是指函数内部的最后一个动作是函数调用。该调用的返回值,直接返回给函数。
  尾递归,函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
  至于为什么很多场景要提倡使用尾调用,为什么使用尾调用和尾递归会有更好的性能呢?,就涉及到了执行上下文栈 的知识。https://github.com/mqyqingfeng/Blog/issues/4:当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。 所以我理解是:也正是因为如此执行上下文被弹出,从而释放了内存,所以性能就得到了提升吧!
  尾调用:
// 尾调用
function f(x) {
return g(x);
}

  注意:

// 非尾调用
function f(x) {
return g(x) + 1;
}
因为g(x)的返回值还需要跟1进行计算后,f(x)才会返回值。
两者仅仅是 + 1这一点点的区别,有什么不一样呢?答案就是执行上下文栈的变化不一样。
为了模拟执行上下文栈的行为,让我们定义执行上下文栈是一个数组:
ECStack = [];
尾调用的执行过程:
// 模拟的伪代码:
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.push(<g> functionContext);
ESStack.pop();
 
非尾调用的执行过程:

ECStack.push(<f> functionContext);
ECStack.push(<g> functionContext);
ECStack.pop();
ECStack.pop();
 
  由此可以看到尾调用和非尾调用都是需要调用f()函数和g()函数,但是,尾调用,当调用了f()之后,马上就释放了f()的执行上下文,执行g()时候,就只剩下g()的执行上下文了,而非尾调用是先把两个函数先后压入执行上下文栈,待最后一个函数执行完才把两个函数的执行上下文弹出(在没有执行完最后一个函数之前,前面的函数执行上下文一直占据着内存)。
  阶乘函数:

// 非尾调用写法:
function factorial(n) {
if (n == 1) return n;
return n * factorial(n - 1);
}
factorial(5) // --> 5 * 4 * 3 * 2 * 1 = 120 // 尾调用优化写法:
function factorial(n, res) {
if (n == 1) return res;
return factorial(n - 1, n * res)
}

javascript专题系列--尾调用和尾递归的更多相关文章

  1. JavaScript中的尾调用优化

    文章来源自:http://www.zhufengpeixun.com/qianduanjishuziliao/javaScriptzhuanti/2017-08-08/768.html JavaScr ...

  2. JavaScript 中的尾调用

    尾调用(Tail Call) 尾调用是函数式编程里比较重要的一个概念,它的意思是在函数的执行过程中,如果最后一个动作是一个函数的调用,即这个调用的返回值被当前函数直接返回,则称为尾调用,如下所示: f ...

  3. ES6 之 函数的扩展 尾调用以及尾递归

    函数参数的默认值 function log(x, y) { y = y || 'world' console.log(x + ' ' + y); } log('hello') // hello wor ...

  4. javascript专题系列--js乱序

    乱序的意思想必没有不知道:就是将数组打乱. 听到乱序一般都会想到js的随机函数Math.random(); var values = [1, 2, 3, 4, 5]; values.sort(func ...

  5. JavaScript 专题系列第六篇,讲解深浅拷贝的技巧和以及实现深浅拷贝的思路

    拷贝也是面试经典呐! 数组的浅拷贝 如果是数组,我们可以利用数组的一些方法比如:slice.concat 返回一个新数组的特性来实现拷贝. 比如:   var arr = ['old', 1, tru ...

  6. JavaScript函数尾调用与尾递归

    什么是函数尾调用和尾递归 函数尾调用与尾递归的应用 一.什么是函数的尾调用和尾递归 函数尾调用就是指函数的最后一步是调用另一个函数. //函数尾调用示例一 function foo(x){ retur ...

  7. Selenium3 + Python3自动化测试系列十——调用JavaScript代码

    调用JavaScript代码 一.调用JavaScript代码方法 Selenium在对浏览器操作时会有自动化代码中不稳定的部分,经常出错的部分,可以将这部分对网页元素进行操作的代码换成对应的Java ...

  8. ES6躬行记(15)——箭头函数和尾调用优化

    一.箭头函数 箭头函数(Arrow Function)是ES6提供的一个很实用的新功能,与普通函数相比,不但在语法上更为简洁,而且在使用时也有更多注意点,下面列出了其中的三点: (1)由于不能作为构造 ...

  9. js 调用栈机制与ES6尾调用优化介绍

    调用栈的英文名叫做Call Stack,大家或多或少是有听过的,但是对于js调用栈的工作方式以及如何在工作中利用这一特性,大部分人可能没有进行过更深入的研究,这块内容可以说对我们前端来说就是所谓的基础 ...

随机推荐

  1. Azure DevOps Server(TFS) 客户端分析

    Azure DevOps Server(TFS) 是微软公司的软件协作开发管理平台产品,为软件研发.测试.实施提供全流程的服务.作为一款应用服务器产品,他的客户端是什么,在哪里下载客户端?我们在项目实 ...

  2. 查看xml源码的方法

    查看xml源码的方法 要通过查看源码才能看到xml源码 因为 print_r输出的时候 默认页面打开是html编码的...... 所以解析不了xml

  3. 转载:RAID5和RAID10,哪种RAID更适合你

    转自 http://storage.it168.com/h/2007-06-28/200706281204046_3.shtml 存储是目前IT产业发展的一大热点,而RAID技术是构造高性能.海量存储 ...

  4. nginx 日志配置log_format用法

    nginx服务器日志相关指令主要有两条: 1.一条是log_format,用来设置日志格式: 2.另外一条是access_log,用来指定日志文件的存放路径.格式和缓存大小,可以参加ngx_http_ ...

  5. vue-cli安装以及创建一个简单的项目(二)(vuex使用、发行一个简单的app)

    1.vuex的使用 vuex是vue的状态管理中心,vuex来保存我们需要管理的状态值,值一旦被修改,所有引用该值的地方就会自动更新,常用于: 1.多个视图依赖同一状态(l例:菜单导航) 2.来自不同 ...

  6. java架构之路-(JVM优化与原理)JVM的运行时内存模型

    还是我们上次的图,我们上次大概讲解了类加载子系统的执行过程,验证,准备,解析,初始化四个过程.还有我们的双亲委派机制. 我们这次来说一下运行时内存模型.上一段小代码. public class Mai ...

  7. 【JS】---5 JS通过事件隐藏显示元素

    JS通过事件隐藏显示元素 在开发中,很多时候我们需要点击事件,才显示隐藏元素.那如何做到页面刚开始就把标签隐藏. 有两种方法: (1) display:none    <div id=" ...

  8. 记录自己运行eShopOnContainers过程中遇到的坑

    由于各种各样的问题,依照官方文档运行eShopOnContainers项目遇到了好多莫名其妙的错误. 好在最后都解决了,在此记录,以防自己以后再遇到,也为遇到同样问题的同学提供参考. 参考的官方文档 ...

  9. 三元運算子回傳lambda expression

    紀錄一下 假如我想要透過三元運算子?: 傳回lambda expression 要明確轉型

  10. python中的三个读read(),readline()和readlines()

    Python 将文本文件的内容读入可以操作的字符串变量非常容易. 文件对象提供了三个“读”方法: .read()..readline() 和 .readlines(). 每种方法可以接受一个变量以限制 ...