2015-06-04 114 views
0

编辑:这个问题主要集中在每个迭代中是否应该在for循环(10亿次的巨大循环)中声明对象,还是它最好在外部声明一次对象循环以节省对象声明期间(每次sprint中花费在内存分配上的时间)的时间。更好地在时间优化方面声明JavaScript对象for循环或外部

我想优化我的代码,确保我没有花费任何额外的时间为每个迭代中for循环中声明的对象分配内存。

我有一个很长的循环(说亿的顺序),它在每个数组迭代中创建巨大的对象,并推动这些对象到一个数组。我的问题是,如果最好在循环中声明对象或在外部声明它,以便JavaScript运行时不必为每个sprint中的对象分配内存。

这是我为百万计循环尝试的东西。尝试了十亿计数,但它并没有我的电脑上完成:

//Case 1: Object declared outside the for loop 
function createObjInForLoopWithVarDeclaredOutside() { 

    var startTime = Date.now(); 
    var obj; //object declared here so that memory allocation is done one time 

    var targetArray = []; 

    for(var i = 0; i < 1000000; i++) { 

     obj = {}; 

     obj.id = i; 
     obj.value = 'value :: ' + i; 

     targetArray.push(obj); 

    } 

    var endTime = Date.now(); 

    var timeTaken = endTime - startTime; 
    console.log('Time taken: ' + timeTaken); 

} 

时间由上述程序采取:505至525毫秒

//Case 2: Object declared and defined inside for loop for each sprint 
function createObjInForLoopWithVarDeclaredInside() { 

    var startTime = Date.now(); 

    var targetArray = []; 

    for(var i = 0; i < 1000000; i++) { 

     var obj = {}; 

     obj.id = i; 
     obj.value = 'value :: ' + i; 

     targetArray.push(obj); 

    } 

    var endTime = Date.now(); 

    var timeTaken = endTime - startTime; 
    console.log('Time taken: ' + timeTaken); //486 to 509 

} 

时间由上述程序采取:486至509毫秒

有人可以帮忙解释哪一种更好的方法来优化执行时间?第二种方法是在for循环中声明对象,我担心这可能需要更长的时间才能运行,因为每次迭代时,运行时都必须为新对象分配内存,这可能需要更长的时间。然而,使用我的分析数据,似乎在for循环内声明对象的第二种方法更好。在范围界定方面,我个人最喜欢的是在需要时对变量进行decalre,但对于如此巨大的for循环,我想查看各种运行时优化。

如果JavaScript在运行代码之前优化代码,我不确定是否需要此优化。

+1

我会虚心地问为什么这个问题被拒绝投票的原因?我是新来的Stackoverflow,但我尽我所能将我所有的分析和示例代码放在我的问题中解释我的问题。如果我知道这里缺少什么,我将很感激,以便我可以将它用作未来的输入。谢谢! –

回答

1

两者都是一样的。 for循环不创建新的作用域,因此变量被提升到包含函数作用域的顶部。

例如:

> i 
undefined 
> for (var i = 0; i < 5; i++) { var test = i; } 
undefined 
> i 
5 
> test 
4 

正如你所看到的,变量itest在这里提升到全球范围。您可能想要查看You Don't Know JS: Scopes & Closures以获得更好的理解。

+0

感谢杰森的回复!它有助于理解提升和范围的概念。顺便说一下你的文章中的代码格式是什么?好像你正在试图解释提示,如果你在你的代码片段中添加了一些注释,它会有更多的帮助。 –

+0

@PrabhashRathore,格式只是来自浏览器控制台或节点的REPL的输出。我很高兴这有助于你的理解和快乐的编码! –

1

这些完全一样。 JS中的变量声明被提升到周围函数范围的顶部。

对象的实际分配将成为昂贵的部分,您必须在每次迭代中都这样做。

声明变量,最坏的情况是在函数堆栈上放置一个引用。不管你怎么做,这都很便宜,但是因为JS强制提升,所以你可能不止一次执行这个操作的唯一方法就是将对象创建分成另一个函数。如果你这样做了,在十亿次迭代中,通话开销会自己消灭性能。

+0

感谢您的回复!我没有考虑Java背景下的变量提升。另外,因为for循环不会创建一个新的作用域,所以两个函数都是一样的。 –

0

当你基准的东西,你应该只是尽可能孤立行为的差异。

你在你的基准

  1. 已经混合的担忧初始化对象
  2. 设置对象
  3. 推对象数组的属性

这似乎是主要的目标是基准对象创建。现在,hoisting不仅对每个示例都具有相同的确切基准,但如果targetArray.push消耗90%的循环时间会怎样?无论如何,您已将结果的准确性稀释为完全无意义的数字。

您还在比较单个样本。尝试使用像jsperf这样的基准测试工具,它将获取多个样本并计算/显示数据中的任何差异。

+0

感谢naomik对基准和jsperf的投入!我同意,为了对一个算法进行基准测试,我们应该关注主要算法,这是基准测试所关注的领域。但是在这种情况下,我更感兴趣的是了解var声明在循环内部还是外部的含义。但是学习JS中的提升和范围界定值得我的关注。另外,即使我有其他操作,如创建对象和推送到数组,但这两种算法都可用,因此在两种算法中执行这些操作所需的时间将相互抵消。 –

+0

@PrabhashRathore只是为了说明,操作A需要'0.01秒',而操作B需要'0.02'。 A和B的两个测试都使用'arr.push',它的取值为0.1秒。测试A的总和为0.11,测试B的总和为0.12。结果使他们看起来好像在性能上几乎相当:'0.11'只比** 0.12快** 1.09x **,但操作A实际上是** 2x **比操作B更快(“0.01”vs '0.02')。 – naomik

+0

然而,因为你在其他操作(如'arr.push')中混合使用,所以难以看清差异。也就是说,A和B之间的*有效*差异由于每次测试中运行的其他操作而被“稀释”。 – naomik