最近在看<JavaScript高级程序设计>中看到arguments.callee这个属性,才知道JavaScript里面的递归有这么多的坑。以前都不知道,今天就整理一下,我们先从最初开始吧。
用递归实现一个阶乘函数
这里就不用说定义了,咱们直接上代码,在正常模式下。
function factorial(num){
if(num<=1){
return 1;
}else{
return num * factorial(num-1);
}
}
上面是一个典型的递归调用,可但是,它有一个问题。
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));//报错
递归函数就是在一个函数里通过名字调用自身的情况(还是得说一下)。正常的情况下我们这样做没有问题了,可是关键是JavaScript
语言和其他的语言不太一样,函数名称只是一个指针,它并不是函数的实体对象。所以如果我们把这个指针改变了,那么在函数里面的指针调用就不是它自己了,上面的例子里,我们直接把它干掉了。让它指向了空,它直接就奔溃了。
这里就引出了一个重要的属性:
arguments.callee
callee
是arguments
对象的一个属性,arguments.callee
值一个指向正在执行的函数的指针。因此它可以实现对函数的递归调用。
例如:
function factorial(num){
if(num<=1){
return 1;
}else{
return num * arguments.callee(num-1);
}
}
通过使用arguments.callee
代理函数名,可以确保无论怎样调用函数都不会出问题。因此在正常模式下,使用这种方式会更加保险。
但是在严格模式下,调用上面的arguments.callee
却会导致错误,因为严格模式不支持arguments
。那怎么样办呢 ?
严格模式下无法使用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)
下一步就是怎么解决这个问题。答案是使用尾调用优化。
什么是尾调用呢?可以先看这个什么是尾调用?
网友评论