为什么do-while循环比javascript中的函数调用运行得更快?

时间:2016-12-10 17:33:58

标签: javascript node.js

所以,我试图在javascript中模拟来自Ruby的块的概念。我想到了一个使用do while循环的想法,它只在while段中有一个false条件而执行一次。这样,无论任何变量如何,括号中的代码将始终执行但只执行一次。另外,我想将时间与具有相同行为的常规函数​​调用进行比较。这是我使用的代码:

//code block time tests
var i = 0;
var start = new Date().getTime()/1000;
while(i < 100000){
  (function(){i++;})()
}
console.log((new Date().getTime()/1000)-start);

//function call test 0.016000032424926758
var j = 0;
var start = new Date().getTime()/1000;
while(j < 100000){
  do {j++;}while(false);
}
console.log((new Date().getTime()/1000)-start);


//do once test 0.002000093460083008

然而,似乎函数调用比do while循环慢得多。为什么是这样?函数调用不应该比while循环更优化吗?

2 个答案:

答案 0 :(得分:3)

问题不在于调用一个函数很慢;它是创建一个函数很慢。您现有的代码会创建一个新的匿名函数,并在每次执行循环时执行它。这比定义函数一次然后多次调用它慢得多,因为函数创建相对较慢。

// TEST 1
// Your existing first test creates an anonymous function on every run
//code block time tests
var i = 0;
var start = new Date().getTime()/1000;
while(i < 100000){
  (function(){i++;})()
}
console.log((new Date().getTime()/1000)-start);
// something like 0.009999990463256836 seconds

// TEST 2
// Your second test, with no function at all
var j = 0;
var start = new Date().getTime()/1000;
while(j < 100000){
  do {j++;}while(false);
}
console.log((new Date().getTime()/1000)-start);
// something like 0.0010001659393310547 seconds

// TEST 3
// Here's what happens with a named function, defined only once
function aRealFunction(n) {
    return n+1;
}
var i = 0;
var start = new Date().getTime()/1000;
while(i < 100000){
  i = aRealFunction(i);
}
console.log((new Date().getTime()/1000)-start);
// something like 0.0009999275207519531 seconds - ten times faster

答案 1 :(得分:2)

  

所以,我试图在javascript中模拟来自Ruby的块的概念。我想到了一个想法,即使用do while循环,只在while段中有一个false条件执行一次。

仅供参考,这没有任何意义。 :-)

之间没有区别
do {
    /* something */
}
while (false);

{
    /* something */
}

除了前者涉及更多代码和无意义测试。在JavaScript(以及使用类似语法的大多数其他语言,如Java,C#,C ++和C)中,块不需要与任何东西相关联,它可以是独立的。

  

然而,似乎函数调用比do while循环慢得多。这是为什么?

很多原因:

  1. 您不仅在循环中调用该功能,而且还在循环中创建

    < / LI>
  2. 对函数的每次调用都涉及开销:为本地创建一个新的执行上下文和关联的绑定对象,填充它,设置this的值等

  3. 从函数中引用i要求JavaScript引擎首先查看函数调用执行上下文的绑定对象,然后,如果它没有找到{{ 1}},按照从该上下文到封闭上下文的链接,以便它可以检查绑定对象i。 (例如,关闭链后。)

  4. 虽然JavaScript引擎(在本例中为V8)可以优化这些功能,但默认情况下,V8的内置编译器是一个两阶段优化编译器:它执行初始快速编译,这是正确但不一定优化,然后返回并优化事物,如果它们看起来像热点。某事物是否是热点的门槛是复杂的,但Crankshaft(优化编译器)可能不会在第10,000次迭代之前启动,因此您无法看到该优化的所有好处。

    附注:从ES2015开始,如果一个块包含任何块范围的声明(iletconst等),则该块将获得自己的绑定对象那些声明。 (ES5及更早版本没有阻止范围。)