1. 程式人生 > >遞歸函數詳解

遞歸函數詳解

func lee all mas 參數 而且 pos 並且 函數詳解

1、什麽是遞歸函數?

遞歸函數就是在一個函數通過名字調用自身的情況下構成的,

如下所示:我們用遞歸實現階乘

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

2、如何實現遞歸函數?

(1)先寫一層的情況。上面所示的遞歸階乘我們就可以先思考num參數乘num-1的情況。

(2)抽象遞歸參數。在遞歸函數中,如何將下一層關聯起來就需要抽象參數來進行解決,參數的個數根據情況而定。

(3)找到突破點。為什麽要找一個突破點?在js中你會發現很少有人寫遞歸,因為不註意就會造成死循環。必須記住遞歸函數是自己調用自己,某種情況(這種情況肯定存在)不調用自己。在上面的遞歸函數中num<=1就是一個突破點。

3、上述代碼存在的問題

    var anotherfactorial = factorial;
    factorial = null;
    console.log(anotherfactorial(3)); // factorial is not a function(出錯)

為什麽會出錯呢?

以上代碼先把factorial函數保存在變量anotherfactorial中,然後將factorial賦值為null。導致的結果指向原始函數的引用只剩下一個。但是接下來調用anotherfactorial時,由於必須執行factorial函數,而factorial這時候已經賦值為null了,所以就會導致錯誤。

那麽如何解決這個問題呢?

在ECMAScript提供了arguments.callee可以解決這個問題。arguments.callee是一個指向正在執行的函數(anotherfactorial)的指針,因此可以用它來實現對函數的遞歸調用。例如上述代碼我們就可以這樣寫:

    var factorial = function (num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * arguments.callee(num - 1);
        }
    }
    
var anotherfactorial = factorial; factorial = null; console.log(anotherfactorial(3)); // 6

通過使用arguments.callee代替函數名,可以確保無論怎樣調用函數都不會出問題。因此在使用遞歸函數時,使用arguments.callee總比使用函數名更保險。

但是在嚴格模式下,不能直接訪問arguments.callee,並且會導致錯誤。所以產生了終極解決方案,使用命名函數表達式來達成相同的效果。例如:

    var factorial = (function f(num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * f(num - 1);
        }
    });
    var anotherfactorial = factorial;
    factorial = null;
    console.log(anotherfactorial(3)); // 6

在以上代碼中創建了一個名為f的命名函數表達式,避免了出現arguments.callee。然後將它賦值給變量factorial。即使把函數賦值給另一個變量,函數的名字f仍然有效,所以遞歸調用照樣能正確完成,而且這種方法在嚴格模式和非嚴格模式下都能運行。

遞歸函數詳解