2010-05-22 139 views
1

爲什麼下面的代碼運行得如此......慢.......?Javascript尾遞歸

<html><body><script type="text/javascript"> 
    var i = 0; 

    f(); 

    function f() { 
     if (i == 5000) { 
      document.write("Done"); 
     } else { 
      i++; 
      tail(); 
     } 
    } 

    function tail() { 
     var fn = tail.caller; 
     var args = arguments; 
     setTimeout(function() {fn.apply(this, args)}, 0); 
    }; 

</script></body></html> 

回答

15

幾個原因:

  1. 一個函數對象上的使用caller屬性引入的開銷(和是非標準)

  2. 同樣使用arguments僞陣列(IIRC,這將使函數調用減慢2-5倍,具體取決於您使用的瀏覽器)

  3. setTimeout通常需要不少於 10ms才能調用您的函數(儘管Chrome有時會比這更快),即使您指定0作爲超時時間。這可能是最大的原因:在〜10ms的5000次迭代是50秒。

這最後一個項目是我說的原因:

如果你有大量的循環迭代(例如,一對夫婦的幾百,而不是15),它可能是值得做的一大塊它們在每次迭代中而不是在每次迭代時產生;產量需要一段可測量的時間(通常約10-15ms)。

...在我回答your other recent question

請注意,您的代碼可以在不使用callerarguments被改寫,使之更加清晰:

var i = 0; 

f(); 

function f() { 
    if (i == 5000) { 
     document.write("Done"); 
    } else { 
     i++; 
     setTimeout(f, 0); 
    } 
} 

另外:我注意到您的代碼示例中使用document.write。如果您在頁面解析期間執行的代碼(這是唯一可以使用document.write的位置)執行此操作,而不是在頁面解析後使用事件處理程序觸發的代碼中,則不能使用setTimeout。這是因爲通過document.write輸出的代碼具有在頁面加載時與HTML解析器同步運行。正是由於這個原因,腳本塊在解析過程中被執行:如果它們發出HTML,解析器必須處理。如果你想在解析(我建議)之後修改頁面,你想要使用DOM方法來獲取元素引用,然後使用DOM方法添加到它或者(這樣既容易又快速運行)在元素上設置innerHTML屬性。

我來自這兩個問題的感覺是,你可能想尋找到web workers,並且僅使用上述的回退爲don't support them yet(IE,恐怕瀏覽器,但我敢打賭,IE9將有他們  —和所有其他主要的人都有他們)。

+0

非常感謝您的解釋! – 2010-05-23 14:51:48

+0

我一定會考慮Web Workers! – 2010-05-23 15:04:55