一個函數能夠指向並調用自身(call itself)。有三種方法能夠達到這個目的:ide
函數名函數
arguments.calleeui
做用域下的一個指向該函數的變量名.net
上述概念引用自MDN,對遞歸概念不清楚的能夠自行查看;調試
在這裏咱們討論一下遞歸函數中遞歸後的語句如何執行,先看這樣一個例子:code
function rec(x){ if(x!==1){ console.log(x) rec(x-1) console.log(x) } } rec(5) //輸出爲5 4 3 2 2 3 4 5
以上這段代碼執行的結果就是遞歸前的語句順序執行,遞歸後的語句倒序執行。初看到這段代碼,徹底不能理解它執行的順序。經過調試讓代碼逐行執行,能夠看到執行的順序,其實就是一層一層執行遞歸,每執行到rec(x-1)時就從新執行該函數,遞歸後的語句會在遞歸執行到最裏層後再由內向外輸出。
CSDN上的一篇博客很好的總結了遞歸的特性以下:
1 每一次函數調用都會有一次返回.當程序流執行到某一級遞歸的結尾處時,它會轉移到前一級遞歸緊接着的後面繼續執行.
2 遞歸函數中,位於遞歸調用前的語句和各級被調函數具備相同的順序.如打印語句 #1 位於遞歸調用語句前,它按照遞歸調用的順序被執行了 4 次.
3 每一級的函數調用都有本身的私有變量.
4 遞歸函數中,位於遞歸調用語句後的語句的執行順序和各個被調用函數的順序相反.
5 雖然每一級遞歸有本身的變量,可是函數代碼並不會獲得複製.
6 遞歸函數中必須包含能夠終止遞歸調用的語句.blog
在這個例子中,終止遞歸調用的條件是if(x!==1),若是調用時取值x小於1,會構成死循環;這個例子中的遞歸採用了經過函數名調用的方法。
下面咱們再討論一下argumengts.callee方式,在ES5嚴格模式下,callee是沒法使用的,緣由詳見MDN中arguments.callee。遞歸
這是callee的一個獨特的用法,在這個結構下沒法替代,ip
function factorial(num){ if (num <= 1){ return 1; } else { return num * factorial(num-1); } } var anotherFactorial = factorial; alert(anotherFactorial(4)); //出錯!
但咱們能夠用下面這種方式,把遞歸函數賦值給一個變量:作用域
var factorial = (function f(num){ if (num <= 1){ return 1; } else { return num * f(num-1); } });