最近在看<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
callee
是arguments
对象的一个属性,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)
下一步就是怎么解决这个问题。答案是使用尾调用优化。
什么是尾调用呢?能够先看这个什么是尾调用?