遞歸函數就是會直接或者間接調用自身的一種函數。遞歸是一種強大的編程技術,它把一個問題分解爲一組類似的子問題,調用自身去解決它的子問題。javascript
問題描述:有3根柱子和一套直徑各不相同的空心圓盤。開始時源柱子上的全部圓盤都按照從小到大的順序堆疊。目標是經過每次移動一個圓盤到另外一根柱子,最終把一堆圓盤移動到目標柱子上,過程當中不容許把較大的圓盤放置在較小的圓盤上。html
var hanoi=function(disc,src,aux,dst){ if(disc>0){ hanoi(disc-1,src,dst,aux); console.log("move "+disc+" from "+src+" to "+dst); hanoi(disc-1,aux,src,dst); } }
圓盤數量爲3的時候解法爲:java
hanoi函數把一堆圓盤從一根柱子移動到另外一根柱子,必要時使用輔助的柱子。node
它把問題分解成3個子問題:
首先,移動一對圓盤中較小的圓盤到輔助柱子上,從而露出下面較大的圓盤。編程
而後,移動下面較大的圓盤到目標柱子上。數組
最後,它將剛纔較小的圓盤從輔助的柱子上再移動到目標柱子上。瀏覽器
傳遞給hanoi函數的參數包括當前移動的圓盤編號和它將用到的3根柱子。當它調用自身的時候,它去處理當前正在處理的圓盤之上的圓盤。最終,它會以一個不存在的圓盤編號去調用。此時,不執行任何操做。因爲該函數對非法值不予理會,就不用擔憂它會致使死循環。函數
遞歸函數能夠很是高效地操做樹形結構,好比瀏覽器文檔對象模型DOM。每次遞歸調用處理指定樹的一小段。學習
html結構以下:測試
<body> <div class="test">測試div</div> <span class="test">測試span</span> <div class="test1">test1 div</div> </body>
js以下:
<script> /*定義walk_the_DOM函數,它從某個指定的節點開始,按HTML源碼中的順序訪問該樹的每一個節點。 它會調用一個函數,並依次傳遞每一個節點給它。walk_the_DOM調用自身去處理每個子節點。 */ var walk_the_DOM=function walk(node,func){ func(node); node=node.firstChild; while(node){ walk(node,func); node=node.nextSibling; } } /*定義getElementsByAttribute函數。它以一個屬性名稱字符串和一個可選的匹配值做爲參數。 它調用walk_the_DOM,傳遞一個用來查找節點屬性名的函數做爲參數。 匹配的節點會累加到一個結果數組中。 */ var getElementsByAttribute=function(att,value){ var results=[]; walk_the_DOM(document.body,function(node){ var actual=node.nodeType===1&&node.getAttribute(att); if(typeof actual==='string' &&( actual===value|| typeof value!=='string')){ results.push(node); } }); return results; } console.log(getElementsByAttribute("class","test"));//[div.test, span.test]
求階乘的函數:
function factorial(num){ if(num<=1){ return 1; }else{ return num*factorial(num-1); } }
正常狀況運行沒問題,可是下面操做會讓它出錯:
var anotherFactorial=factorial;//把函數保存在遍歷anotherFactorial中 factorial=null;//factorial置爲null,此時指向原始函數的引用只剩一個 anotherFactorial(3)
factorial已經再也不是函數,因此會報錯。
arguments.callee是一個指向正在執行的函數的指針,所以能夠用它來實現對函數的遞歸調用
//console function factorial(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1); } } var anotherFactorial=factorial; factorial=null; anotherFactorial(3) //6
用arguments.callee代替函數名,能夠確保不管怎樣調用函數都不會出問題。所以,在編寫遞歸函數時,使用arguments.callee總比使用函數名更保險。
問題:在嚴格模式下,不能經過腳本訪問arguments.callee,訪問這個屬性會報錯。
更多嚴格模式相關內容可參考:javascript 語句和嚴格模式(三)
建立一個名爲f()的命名函數表達式,而後賦值給factorial,即便把函數賦值給了另外一個變量,函數的名字f仍然有效,因此遞歸調用照樣能正常完成。
這種方式在嚴格模式和非嚴格模式均可行。
//console var factorial =function f(num){ 'use strict' if(num<=1){ return 1; }else{ return num*f(num-1); } } factorial(3) //6 var anotherFactorial=factorial; factorial=null; anotherFactorial(3) //6
命名函數表達式也有它的一些經典bug,見javascript 函數和做用域(函數,this)(六)
本文做者starof,因知識自己在變化,做者也在不斷學習成長,文章內容也不定時更新,爲避免誤導讀者,方便追根溯源,請諸位轉載註明出處:http://www.cnblogs.com/starof/p/6510723.html有問題歡迎與我討論,共同進步。