hn-failte's blog hn-failte's blog
首页
  • 前端文章

    • JavaScript
    • Vue
    • React
    • Webpack
    • 混合开发
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《React》笔记
    • 《TypeScript 从零实现 axios》
    • 《Git》学习笔记
    • TypeScript笔记
    • JS设计模式总结笔记
  • HTML&CSS
  • HTML
  • CSS
  • CSS预处理
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 算法
  • 数据库
  • 操作系统
  • 工具
  • 学习
  • 面试
  • 心情杂货
  • 前端相关
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

hn-failte

前端cv仔
首页
  • 前端文章

    • JavaScript
    • Vue
    • React
    • Webpack
    • 混合开发
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《React》笔记
    • 《TypeScript 从零实现 axios》
    • 《Git》学习笔记
    • TypeScript笔记
    • JS设计模式总结笔记
  • HTML&CSS
  • HTML
  • CSS
  • CSS预处理
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 算法
  • 数据库
  • 操作系统
  • 工具
  • 学习
  • 面试
  • 心情杂货
  • 前端相关
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Webpack

  • React

  • JavaScript

    • new命令原理
    • ES5面向对象
    • ES6面向对象
    • 多种数组去重性能对比
    • JS随机打乱数组
    • 判断是否为移动端浏览器
    • 将一维数组按指定长度转为二维数组
    • 防抖与节流函数
    • JS获取和修改url参数
    • 比typeof运算符更准确的类型判断
    • 三级目录

    • JavaScript

      • JavaScript之函数尾调用与函数尾递归
        • 尾调用
        • 尾递归
        • 兼容
      • 一种实现call、apply、bind的方法
      • 实现call、apply、bind
      • JavaScript闭包详解
      • 窗口间的通信与iframe跨域
      • JavaScript逻辑运算符“&&”和“||”短路原则的应用
      • Object原型梳理
      • JavaScript设计模式之发布&订阅模式
      • JavaScript核心:两链一包
      • JavaScript之正则
      • 字符串拼接性能优化
      • JavaScript变量转换
  • Vue

  • 混合开发

  • 学习笔记

  • 微信小程序

  • 前端
  • JavaScript
  • JavaScript
hn-failte
2020-03-21

JavaScript之函数尾调用与函数尾递归

# 函数尾调用与函数尾递归

偶然重新学习ES6,发现原来在函数方面还有添加尾调用这个特性,尾调用可以减少一次函数调用帧的生成,而众所周知函数递归存在一个内存消耗的问题,如果把尾调用加入到递归中会怎么样呢......

# 尾调用

函数调用会在内存形成一个调用帧,保存调用位置和内部变量等信息。

一个函数内存在其他函数调用,其他函数就会在这个函数上形成调用帧,所有的调用帧形成了一个调用栈。

为尾调用是指在函数的最后一步操作时,返回另一个函数的调用,这个时候,由于不会用到调用的位置和内部变量等信息,不需要保留外层函数的调用帧。

尾调用:

function A() {
    return B() // 尾调用
}

function B() {
    return C() + 1; // 最后一步操作不是函数调用
}

function C(){
    var val = D()
    return val; // 最后一步操作不是函数调用
}
function D(){
    E() // 最后一步操作不是函数调用
}
function E(){
    console.log('只有A是尾调用') // 最后一步操作不是函数调用
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 尾递归

函数递归对内存消耗很大,每次递归都会产生一个调用帧,而整个递归下来会产生很多的调用帧,很容易出现栈溢出的问题。

尾调用是可以有效减少执行栈的,将尾调用和递归结合,有可能将复杂度为O(n)的计算变成O(1)。

非尾调用

function Fibonacci (n) {
    if ( n <= 1 ) {return 1};
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Fibonacci(10) // 89
Fibonacci(100) // 超时
Fibonacci(500) // 超时
1
2
3
4
5
6
7

尾调用

function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
    if( n <= 1 ) {return ac2};
    return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity
1
2
3
4
5
6
7

建议在ES6中对递归进行尾调用优化。

# 兼容

编辑 (opens new window)
#Function
上次更新: 2021/08/05, 12:37:41
四级文件(测试)
一种实现call、apply、bind的方法

← 四级文件(测试) 一种实现call、apply、bind的方法→

最近更新
01
基于 Taro 的微信小程序优化指南
02-16
02
搭建一个极简混合开发架构
08-03
03
使用State Hook
04-06
更多文章>
Theme by Vdoing | Copyright © 2017-2023 hn-failte | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式