我們之前先跟大家介紹過關於php中的遞迴函數、其實遞迴函數通常在後端用的比較多。對於後端開發人員來說,遞迴應該是小菜一碟,很簡單的事情,但是很多前端確實對這個不是很了解。其實,前端中也是常用遞迴的,今天我們就來給大家解析下JavaScript中的遞迴函數!
js遞歸呼叫
// 一个简单的阶乘函数 var f = function (x) { if (x === 1) { return 1; } else { return x * f(x - 1); } };
Javascript中函數的巨大靈活性,導致在遞歸時使用函數名稱遇到困難,對於上面的變數聲明,f是一個變量,所以它的值很容易被替換:
var fn = f; f = function () {};
函數是個值,它被賦給fn,我們期待使用fn(5)可以計算出一個數值,但是由於函數內部仍然引用的是變數f,於是它不能正常工作了。
所以,一旦我們定義了一個遞歸函數,便須注意不要輕易改變變數的名字。
上面談論的都是函數式調用,函數還有其它調用方式,例如當作物件方法調用。
我們常常這樣宣告物件:
var obj1 = { num : 5, fac : function (x) { // function body } };
宣告一個匿名函數並且把它賦值給物件的屬性(fac)。
如果我們想要在這裡寫一個遞歸,就要引用屬性本身:
var obj1 = { num : 5, fac : function (x) { if (x === 1) { return 1; } else { return x * obj1.fac(x - 1); } } };
當然,它也會遭遇和函數呼叫方式一樣的問題:
var obj2 = {fac: obj1.fac}; obj1 = {}; obj2.fac(5); // Sadness
方法被賦值給obj2的fac屬性後,內部仍要引用obj1.fac,於是…失敗了。
換個方式會有所改進:
var obj1 = { num : 5, fac : function (x) { if (x === 1) { return 1; } else { return x * this.fac(x - 1); } } }; var obj2 = {fac: obj1.fac}; obj1 = {}; obj2.fac(5); // ok
透過this關鍵字取得函數執行時的context中的屬性,這樣執行obj2.fac時,函數內部便會引用obj2的fac屬性。
可是函數還可以被任意修改context來調用,那就是萬能的call和apply:
obj3 = {}; obj1.fac.call(obj3, 5); // dead again
於是遞歸函數又不能正常運作了。
我們應該試著解決這個問題,還記得前面提到的一種函數宣告的方式嗎?
var a = function b(){};
這種宣告方式叫做內聯函數(inline function),雖然在函數外沒有宣告變數b,但是在函數內部,是可以使用b()來呼叫自己的,於是
var fn = function f(x) { // try if you write "var f = 0;" here if (x === 1) { return 1; } else { return x * f(x - 1); } }; var fn2 = fn; fn = null; fn2(5); // OK // here show the difference between "var f = function f() {}" and "function f() {}" var f = function f(x) { if (x === 1) { return 1; } else { return x * f(x - 1); } }; var fn2 = f; f = null; fn2(5); // OK var obj1 = { num : 5, fac : function f(x) { if (x === 1) { return 1; } else { return x * f(x - 1); } } }; var obj2 = {fac: obj1.fac}; obj1 = {}; obj2.fac(5); // ok var obj3 = {}; obj1.fac.call(obj3, 5); // ok
就這樣,我們有了一個可以在內部使用的名字,而不用擔心遞歸函數被賦值給誰以及以何種方式被呼叫。
Javascript函數內部的arguments對象,有一個callee屬性,指向的是函數本身。因此也可以使用arguments.callee在內部呼叫函數:
function f(x) { if (x === 1) { return 1; } else { return x * arguments.callee(x - 1); } }
但arguments.callee是一個已經準備被棄用的屬性,很可能會在未來的ECMAscript版本中消失,在ECMAscript 5中"use strict"時,不能使用arguments.callee。
最後一個建議是:如果要宣告一個遞歸函數,請慎用new Function這種方式,Function建構子所建立的函式在每次被呼叫時,都會重新編譯出一個函數,遞歸呼叫會引發效能問題——你會發現你的記憶體很快就被耗光了。
js遞迴函數應用程式
最近在做專案的時候,用到了遞迴函數,用來呼叫json的子節點,把所有json中的子節點包含某個數的object ,都push到一個數組中,然後對其進行綁定。
我是透過如下遞歸呼叫的
var new_array=[]; function _getChilds(data){ if(data.ObjType=="某个数"){ new_array.push(cs_data); } if(data.Childs){ if(data.Childs.length>0){ getChilds(data.Childs) } } } function getChilds(data){ for(var i=0;i<data.length;i++){ _getChilds(data[i]); } }使用方法:getChilds("json数据")
就把json中所有包含某個數的資料push到new_array陣列當中了。
總結:
相信透過對上述的講解,大家對遞迴函數的認識更進階一步、遞迴函數不只可以再在php中使用,在前端JavaScript中同樣可以使用,希望對你有幫助!
相關推薦:
#JavaScript中遞歸函數的細化認識以及範例程式碼分享
以上是JavaScript中遞迴函數的解析與說明的詳細內容。更多資訊請關注PHP中文網其他相關文章!