所以,我试图在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循环更优化吗?
答案 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循环慢得多。这是为什么?
很多原因:
您不仅在循环中调用该功能,而且还在循环中创建。
< / LI>对函数的每次调用都涉及开销:为本地创建一个新的执行上下文和关联的绑定对象,填充它,设置this
的值等
从函数中引用i
要求JavaScript引擎首先查看函数调用执行上下文的绑定对象,然后,如果它没有找到{{ 1}},按照从该上下文到封闭上下文的链接,以便它可以检查其绑定对象i
。 (例如,关闭链后。)
虽然JavaScript引擎(在本例中为V8)可以优化这些功能,但默认情况下,V8的内置编译器是一个两阶段优化编译器:它执行初始快速编译,这是正确但不一定优化,然后返回并优化事物,如果它们看起来像热点。某事物是否是热点的门槛是复杂的,但Crankshaft(优化编译器)可能不会在第10,000次迭代之前启动,因此您无法看到该优化的所有好处。
附注:从ES2015开始,如果一个块包含任何块范围的声明(i
,let
,const
等),则该块将获得自己的绑定对象那些声明。 (ES5及更早版本没有阻止范围。)