2010-05-22 178 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