递归函数也就是在函数内部调用自己,而递归函数如果使用不当,则可能会形成死循环,下面我们就利用阶乘问题来讲一下递归;
下面我们来看一段代码:
<script>
var a=function (x) {
if(x===1){
return 1;
}else{
return x*a(x-1); //当执行此行代码的时候,进行函数的自调用,每次进入运算的数字都比前一个进入的数字小1,其运算过程为5*4*3*2=120
}
};
alert(a(5)); //120
</script>
递归函数不仅仅只有函数的调用,还有其他调用方式,比如作为一个对象方法调用,首先我们需要先声明一个对象,然后声明一个匿名函数并把它赋值与对象的一个属性,代码如下所示:
<script>
var obj = {
num :5,
a :function (x) { //定义一个匿名函数并赋值与对象的a属性
}
};
</script>
如果我们想要在这里面写一个递归,就需要引用属性本身 ,通过this关键字获取函数执行时的context中的属性,这样当将obj中的内容赋值与其他的变量时,函数内部的this会指向当前函数;下面我来补充一下this的知识,它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。
<script>
var obj = {
num :5,
a :function (x) {
if(x===1){
return 1;
}else{
return x*this.a(x-1);
}
}
};
</script>
由于JavaScript中的函数具有灵活性,如果我们将下面的a()赋值给其他的变量,然后在创建一个a()函数表达式时,就会出现错误。
<script>
var a=function (x) {
if(x===1){
return 1;
}else{
return x*a(x-1);
}
};
var fn=a;
a=function () {
};
alert(fn(3)); //NaN
</script>
因为在内部调用的是a();此时的函数作为一个值被赋给其他的变量,而内部调用的还是a();所以出现了运行之后的结果不是我们想要的结果问题。所以,一旦我们定义了一个递归函数,便需要注意不要轻易改变变量的名字。
可是函数还是可以被任意修改context来调用的,那么此时,递归就要用到函数表达式的声明方式,利用函数名来调用自身。
<script>
var a=function b(x) {
if(x===1){
return 1;
}else {
return x*b(x-1); //调用当前函数的函数名,无论函数作为值被赋给谁,对于内部的代码都不会影响
}
};
</script>
下面来补充一个点,也可以解决上面的问题,但是这个属性是一个已经快要被弃用的属性,在ECMAscript 5中”use strict”时,不能使用arguments.callee。可能在以后的版本中会消失,所以不推荐使用,但是可以了解一下以便于在看到其他人的代码中有用到的时候不至于一脸懵逼(我们老师遇到这样的知识点常常这样跟我们说)
Javascript函数内部的arguments对象,有一个callee属性,指向的是函数本身。因此也可以使用arguments.callee在内部调用函数
<script>
function a(x) {
if (x === 1) {
return 1;
} else {
return x * arguments.callee(x - 1);
}
}
alert(a(3)); //6
</script>