之前偶然接触到了这么一个知识点,一开始还以为是非常晦涩的概念,不过熟悉之后其实也比较简单。

# 前言

我们都知道在调用栈这么一个东西,每个函数的调用,实际上会创建一个调用帧放入调用栈之中,等调用结束,这个帧就从栈中 “弹出”

调用帧其实是用来保存一些信息的,比如调用位置、内部变量等。

比如说如果一个函数 A 调用函数 B,那么在 A 的调用帧之上,会创建一个 B 的调用帧。

如果程序内部大量的调用函数而又不释放调用帧,就会占据大量的内存资源。

# 尾调用

为了尽可能的释放调用帧,于是就提出了尾调用这么一个概念。

简单来说,尾调用其实也就一句话,就是在函数的内部调用之中,最后一步操作是调用另外一个函数不涉及其他操作

尾调用之所以与普通的调用不同,是因为它最后一步是调用另外一个函数,这个时候已经不需要使用到该函数的调用位置、内部变量等信息,它的调用帧可以被尾调用中调用的函数取代,而不是新建一个调用帧,从而减少了资源消耗。

举个例子

function f(){
  let i = 1,j=2;
  return g(i+j);
}
f();
// 上面的代码等同于下面这个
function f(){
  return g(3);
}
f();
// 最后等同于
g(3);

# 尾递归

尾递归实际上是尾调用的特殊情况。

调用的概念是一个函数调用其他函数,递归是函数调用自身。

所以尾递归不过就是函数最后一步是调用自身而已。

# 进行尾调用 / 递归优化

举个例子,正常的阶乘函数,我们大家都会写:

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

但是这么写的话实际上会浪费很多资源,因为调用帧不能被释放(需要保存 n),我们如果要将它优化成尾递归写法的话,就不能有其他计算操作。

既然是需要传递信息,我们只需要将需要保存的信息作为参数传递下去不就好了?

function factorial(n,total){
  if(n===1)return total;
  return factorial(n-1,total*n);
}
factorial(5,1);

但是这么写也有问题,就是不利于理解。(为什么计算阶乘需要传递两个参数?)

如果是为了简单的话,可以使用参数默认值,将它定义成这样:

function factorial(n,total = 1){
  if(n===1)return total;
  return factorial(n-1,total*n);
}

这样就可以单参数调用了。

# 函数柯里化

函数柯里化是一种将多参数的函数改写成单参数、或者说更少参数的函数的一种技术。

我们也可以使用函数柯里化将优化过后的阶乘函数改写一下。

// 写一个将参数函数柯里化的工具函数
function currying(fn,n){
  return function(m){
    return fn.call(this,m,n);//n 是固定参数
  }
}
const factorial = currying(factorial,1);
factorial(4);//24
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

orange 微信支付

微信支付

orange 支付宝

支付宝

orange 贝宝

贝宝