遞歸函數也就是在函數內部調用自己,而遞歸函數如果使用不當,則可能會形成死循環,下面我們就利用階乘問題來講一下遞歸;
下面我們來看一段代碼:
<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>