尾调用优化(Tail Call Optimization)

尾调用是指函数的最后一条语句是函数调用,比如下面的代码:

function doSomething() {
return doSomethingElse();
}

在ES5中,尾调用和其他形式的函数调用一样:脚本引擎创建一个新的函数栈帧并且压在当前调用的函数的栈帧上面。也就是说,在整个函数栈上,每一个函数栈帧都会被保存,这有可能造成调用栈占用内存过大甚至溢出。

尾递归在ES6中有何不同(How Tail Call are Different in ECMAScript6)

在ES6中,strict模式下,满足以下条件,尾调用优化会开启,此时引擎不会创建一个新的栈帧,而是清除当前栈帧的数据并复用:

  (1、尾调用函数不需要访问当前栈帧中的变量

  (2、尾调用返回后,函数没有语句需要继续执行

  (3、尾调用的结果就是函数的返回值

下面例子中的函数就可以使用尾调用优化:

"use strict";

function doSomething() {
return doSomethingElse();
}

然而下面例子中的函数不能使用尾调用优化,因为尾调用的结果不是函数的返回值:

"use strict";

function doSomething() {
doSomethingElse();
}

尾调用返回后,如果函数仍然有语句要执行,也是不能使用尾调用优化的:

"use strict";

function doSomething() {
return 1 + doSomethingElse();
}

尾调用函数如果是闭包函数,也不能使用尾调用优化:

"use strict";

function doSomething() {
var num = 1;
var func = () => num;
return func();
}

如何利用尾调用优化(How to Harness Tail Call Optimization)

尾调用优化主要用在递归函数中,通常能带来显著的效果。看一个递归计算阶乘的函数:

function factorial(n) {
if (n <= 1) {
  return 1;
} else {
  return n * factorial(n - 1);
}
}

很明显,上面的代码并不能使用尾调用优化,因为factorial(n-1)返回后,仍然有语句要继续执行。但是我们可以改写这个函数,使其能够利用尾调用优化特性:

function factorial(n, p = 1) {
if (n <= 1) {
  return 1 * p;
} else {
  let result = n * p;
  return factorial(n - 1, result);
}
}

如和改写递归函数以便利用尾调用优化是需要技巧的。如果可以,应该充分利用引擎的尾调用优化特性来优化递归函数。

《理解 ES6》阅读整理:函数(Functions)(八)Tail Call Optimization的更多相关文章

  1. 《理解 ES6》阅读整理:函数(Functions)(五)Name Property

    名字属性(The name Property) 在JavaScript中识别函数是有挑战性的,因为你可以使用各种方式来定义一个函数.匿名函数表达式的流行使用导致函数调试困难,在栈信息中难以找出函数名. ...

  2. 《理解 ES6》阅读整理:函数(Functions)(一)Default Parameter Values

    对于任何语言来说,函数都是一个重要的组成部分.在ES6以前,从JavaScript被创建以来,函数一直没有大的改动,留下了一堆的问题和很微妙的行为,导致在JavaScript中使用函数时很容易出现错误 ...

  3. 深入理解ES6箭头函数中的this

    简要介绍:箭头函数中的this,指向与一般function定义的函数不同,比较容易绕晕,箭头函数this的定义:箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定. 1.何为定义时 ...

  4. ES6 入门系列 - 函数的扩展

    1函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法. function log(x, y) { y = y || 'World'; console.log( ...

  5. es6中的函数

    ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面. function log(x, y = 'World') { console.log(x, y); } log('Hello') // ...

  6. asp.net MVC helper 和自定义函数@functions小结

    asp.net Razor 视图具有.cshtml后缀,可以轻松的实现c#代码和html标签的切换,大大提升了我们的开发效率.但是Razor语法还是有一些棉花糖值得我们了解一下,可以更加强劲的提升我们 ...

  7. asp.net MVC 自定义@helper 和自定义函数@functions小结

    asp.net Razor 视图具有.cshtml后缀,可以轻松的实现c#代码和html标签的切换,大大提升了我们的开发效率.但是Razor语法还是有一些棉花糖值得我们了解一下,可以更加强劲的提升我们 ...

  8. 《javascript个人理解,个人整理。》

    万事开头难. 本人做前端工程师,已几年,没有特别大的,已文字方式去做总结. 前段时间,早已经想好,但是迟迟没有去下笔!好在现在陆陆续续的写下去. 我知道这是一个很大的工程,但是我还是想做下去,不为别的 ...

  9. ES6知识整理(4)--数组的扩展

    最近工作比较忙,基本每天都会加班到很晚.处理一些客户端兼容问题以及提升用户体验的优化.也将近一周没更文了,现在继续es6的学习总结. 上篇回顾 ES6知识整理(三)--函数的扩展 扩展运算符 形式是3 ...

随机推荐

  1. jQuery与Ajax的应用——《锋利的jQuery》(第2版)读书笔记3

    第6章 jQuery与Ajax的应用 jQuery对Ajax操作进行了封装,在jQuery中$.ajax()方法属于最底层的方法,第2层是load().$.get()和$.post()方法,第3层是$ ...

  2. 09-JAVA中的异常处理

    1. 程序执行结果: 也就是它根本就没抛出异常,更别提捕获异常了.那么,为什么会这样呢? 原来, 如上面程序展示,程序运行到k=i/j;的时候,就会直接终止,根本就不会运行到监视的程序,更不会运行到捕 ...

  3. easyui 使用问题积累

    一.<a>标签中onclick和javascript事件与grid的select事件执行的先后顺序 html: <a href='javascript:func();' onclic ...

  4. [Note] changing building platform from vs 2013 to vs community 2015

    The error turned out as "undefined linkage"(The same as you haven't use some function that ...

  5. effetive C++ 02 尽量以const,enum,inline替换#define

    #define ASPECT_RATIO=1.1const double AspectRatio=1.1 1. 使用宏会导致预处理器把程序代码中的ASPECT_RATIO无条件替换为1.1,编译器从未 ...

  6. 解压版MySQL5.7.1x的安装与配置

    解压版MySQL5.7.1x的安装与配置 MySQL安装文件分为两种,一种是msi格式的,一种是zip格式的.如果是msi格式的可以直接点击安装,按照它给出的安装提示进行安装(相信大家的英文可以看懂英 ...

  7. Mysql命令集

    mysql远程授权GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123.com' WITH GRANT OPTION;flush p ...

  8. android六大框架

    -LinearLayout线性布局 垂直排序,每行仅包含一个界面元素 水平排序,每列仅包含一个界面元素 orientation,Layout-weight,Layout-margin(外边距,与屏幕) ...

  9. php设计模式--单例模式

    单例模式(Singleton Pattern 单件模式或单元素模式) 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式是一种常见的设计模式,在计算机系统中,线程池.缓 ...

  10. 使用html2canvas实现批量生成条形码

    /*前台代码*/ <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Generat ...