尾调用和尾递归

ES6 有两个新的东西,前端面试的时候偶尔会问道。之前也有在阮一峰的书上看过几次,但是没有统一归纳学习,今天归纳了一下。

尾调用 : 指函数最后一步调用另一个函数;

function f(x){
  return g(x)
};
  • 下面三个都不是属于尾调用
function a(x){
  let y = b(x);
  return y;
}
//调用后有赋值操作不属于尾调用
function c(x){
  return d(x) + 1;
}
//调用后有运算操作
function a(x){
  g(x);
}
//实际代码相当于
/*** 
  function a(x){
    g(x);
    return undefined;
  }
*/
  • 尾调用不一定要出现在函数尾部,只要最后是一步操作即可
function f(x) {
  if (x > 0) {
    return m(x)
  }
  return n(x);
}

尾调用优化

  • 为什么要用尾调用?尾调用优化代码的意义在哪里。
    函数在调用的时候会在调用栈(call stack)中存有一条记录,每一条记录叫做一个调用帧(call frame),每调用一个函数,就向栈中push一条记录,函数执行结束后依次向外弹出,直到清空调用栈。
function a() {
  console.log('如果有帮助请点个赞,大兄弟');
}
function b() {
  a()
}
function c() {
  b()
}
c();
思维类图

造成这样的结果是因为每个函数在调用另一个函数的时候,没有return该调用,所以执行引擎会认为你还没有调用完毕,会保留调用帧。

而如果使用尾调用优化,调用帧就永远只有一条,这个时候就会节省很大一部分的内存空间,维护了代码运行的流畅性。

function a() {
  console.log('如果有帮助请点个赞,大兄弟');
}

function b() {
  return a()
}

function c() {
  return b()
}
c();
image.png

以上代码就叫做尾调用优化,这个时候调用帧就永远只有一条,节省了部分内存。

尾递归

函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

function test(){
  return test(); //尾递归
}
  • 计算一个数的阶乘,使用尾递归调用的优化
function multiplication(n) {
  if (n === 1) return 1;
  return n * multiplication(n - 1)
}
multiplication(4)//24

//尾递归
function multiplication1(n,init){
  if(n === 1)return init;
  return multiplication1(n -1 ,n*init)
}
multiplication(4,1)//24

//优化
function multiplication2(n,init=1){
  if(n === 1)return init;
  return multiplication2(n -1 ,n*init)
}
multiplication2(4);//24
  • Fibonacci 数列使用尾递归优化

Fibonacci 数列:指的是这样一个数列:/1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*);

//递归形式Fibonacci 数列()
function Fibonacci (n) {
  if ( n <= 1 ) {return 1};

  return Fibonacci(n - 1) + Fibonacci(n - 2);
}

Fibonacci(10) // 89
Fibonacci(100) // 堆栈溢出
Fibonacci(500) // 堆栈溢出

//尾递归优化

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 
//在Javascript中,超出 1.7976931348623157E+103088 的数值即为Infinity,
//小于-1.7976931348623157E+103088 的数值为无穷小。
  • 参考链接:
    阮一峰ES6
    阮一峰网络博客

http://es6.ruanyifeng.com/#docs/function
http://www.ruanyifeng.com/blog/2015/04/tail-call.html

你可能感兴趣的:(尾调用和尾递归)