javascript遞歸

遞歸函數就是會直接或者間接調用自身的一種函數。遞歸是一種強大的編程技術,它把一個問題分解爲一組類似的子問題,調用自身去解決它的子問題。javascript

1、漢諾塔

問題描述:有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根柱子。當它調用自身的時候,它去處理當前正在處理的圓盤之上的圓盤。最終,它會以一個不存在的圓盤編號去調用。此時,不執行任何操做。因爲該函數對非法值不予理會,就不用擔憂它會致使死循環。函數

2、DOM遍歷

遞歸函數能夠很是高效地操做樹形結構,好比瀏覽器文檔對象模型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]

3、命名函數表達式和遞歸

一、遞歸問題

求階乘的函數:

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實現遞歸

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有問題歡迎與我討論,共同進步。

相關文章
相關標籤/搜索