不积跬步之漫谈JavaScript的递归函数

最近在看<JavaScript高级程序设计>中看到arguments.callee这个属性,才知道JavaScript里面的递归有这么多的坑。之前都不知道,今天就整理一下,咱们先从最初开始吧。

用递归实现一个阶乘函数

这里就不用说定义了,我们直接上代码,在正常模式下。es6

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

上面是一个典型的递归调用,可可是,它有一个问题。函数

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));//报错

递归函数就是在一个函数里经过名字调用自身的状况(仍是得说一下)。正常的状况下咱们这样作没有问题了,但是关键是JavaScript语言和其余的语言不太同样,函数名称只是一个指针,它并非函数的实体对象。因此若是咱们把这个指针改变了,那么在函数里面的指针调用就不是它本身了,上面的例子里,咱们直接把它干掉了。让它指向了空,它直接就奔溃了。优化

这里就引出了一个重要的属性:spa

arguments.callee

calleearguments对象的一个属性,arguments.callee值一个指向正在执行的函数的指针。所以它能够实现对函数的递归调用。设计

例如:代理

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num * arguments.callee(num-1);
    }
}

经过使用arguments.callee代理函数名,能够确保不管怎样调用函数都不会出问题。所以在正常模式下,使用这种方式会更加保险。指针

可是在严格模式下,调用上面的arguments.callee却会致使错误,由于严格模式不支持arguments。那怎么样办呢 ?code

严格模式下没法使用arguments.callee怎么办 ?

经过使用命名表达式也但是实现一样的效果。看代码:对象

var factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

上面的代码建立了一个名为f()的命名表达式,而后将它赋值给变量factorial。这样的话,即便把变量的变成另外一个变量,也不会影响我内部本身的调用。f函数名字仍然有效。因此递归函数调用照样能正确执行。递归

如今咱们把ECMA的版本升级到6.

ES6的状况下呢?

ES6的状况下,咱们能够写成另外的方式来实现。例如:

const factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

咱们把var升级为const,让这个变量变成常量,这样变量也改变不了。咱们用箭头函数来替代上面的命名函数。

const factorial =num=>{
     if(num<=1){
        return 1;
    }else{
        return num * factorial(num-1);
    }
}

常量没法更改,因此咱们能够写成上面的代码。可是你觉得代码就没有问题了吗???

咱们这样调用一下:

factorial(100000);

而后报下面的错误,RangeError:超出了最大调用堆栈大小

RangeError: Maximum call stack size exceeded
    at factorial (H:\company_work_space\Demos\Demo1.js:40:18)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)

下一步就是怎么解决这个问题。答案是使用尾调用优化。
什么是尾调用呢?能够先看这个什么是尾调用?

相关文章
相关标签/搜索