for-in vs Object.key forEach没有继承属性

时间:2014-07-31 07:08:58

标签: javascript inheritance

我正在查看Object.keys + forEach vs for-in与正常对象的性能基准。

This基准测试显示,Object.keys + forEachfor-in方法强<62> 。但是,如果您不想获取继承的属性,该怎么办? for-in包含所有非本机继承对象,因此我们必须使用 hasOwnProperty 进行检查。

我试图让另一个benchmark here做到这一点。但现在,for-in方法Object.keys + forEach慢<41%


更新

上述测试是在Chrome中完成的。再次测试,但使用Safari,我得到了不同的结果:Object.keys(..).forEach(..) 34% slower,奇数。

  

注意:我进行基准测试的原因是检查它与 Node.js 的关系。

问题:

  • Node.js Chrome jsperf结果是否相当可观?
  • 发生了什么事,为什么单个条件使for-in方法慢了41% Chrome 中的Object.keys + forEach

6 个答案:

答案 0 :(得分:31)

node.js使用的是V8,虽然我猜它与Chrome中的当前版本不一样,但我想这是节点在该主题上的表现的良好指标。

其次,您正在使用forEach,这在开发时非常方便,但每次迭代都会添加一个回调,而且这是一个(相对)长度的任务。所以,如果你对表演很感兴趣,为什么不用正常的for循环呢?

for (var i = 0, keys = Object.keys(object); i < keys.length; i++) {
    // ...
}

这样可以获得最佳性能,也可以解决Safari中的速度问题。

简而言之:它不是有条件的,而是hasOwnProperty的调用会产生影响。您在每次迭代时都在进行函数调用,这就是for...in变慢的原因。

答案 1 :(得分:15)

请注意:

var keys = Object.keys(obj), i = keys.length;

while(--i) {
   //
}

不会针对索引0运行,然后您将错过其中一个属性。

这在像[&#34; a&#34;,&#34; b&#34;,&#34; c&#34;,&#34; d&#34;]这样的数组中只会运行d, c,b,你会错过&#34; a&#34; &#39;原因索引为0,0为假。

你需要在检查后减少:

var keys = Object.keys(obj), i = keys.length;

while(i--) {
   //
}

答案 2 :(得分:2)

对于任何仍然关注JS中迭代对象属性的人来说,绝对最快的方法是:

var keys = Object.keys(obj), i = keys.length;

while(--i) {
   //
}

http://jsperf.com/object-keys-foreach-vs-for-in-hasownproperty/8

通过不必重新计算密钥数组的长度值(现代浏览器优化几乎可以忽略不计),您可以在大型对象上保存一点,对于简单的for循环也是如此。递减的while循环仍然比for循环更快或者使用长度上限比较增加while循环,这是一个合理的余量。

答案 3 :(得分:2)

我今天也对此感兴趣,但主要是因为我不想用hasOwnProperty测试来传递默认的lint,因为我已经知道我的对象是干净的(因为它们是从对象文字)。无论如何,我对@styonsk的答案进行了一些扩展,以包含更好的输出并运行多个测试并返回输出。

结论:节点很复杂。最好的时间看起来像在Nodejs v4.6.1上使用带有数字for循环或while循环的Object.keys()。在v4.6.1中,带有hasOwnProperty的forIn循环是最慢的方法。但是,在节点v6.9.1上它是最快的,但它仍然比v4.6.1上的Object.keys()迭代器慢。

备注:这是在2013年末的MacBook Pro上运行的,配备16GB内存和2.4Ghz i5处理器。在测试期间,每个测试都固定100%的单个cpu,平均rss约为500MB,峰值为1GB的rss。希望这有助于某人。

以下是针对nodejs v6.9.1和v4.6.1运行的结果,其中包含大对象(10 ^ 6个属性)和小对象(50个属性)

  

具有大对象10 ^ 6属性的节点v4.6.1

     

testObjKeyWhileDecrement   测试次数: 100   总时间:57595毫秒   平均时间: 575.95毫秒

     

testObjKeyForLoop   测试次数: 100   总时间:54885毫秒   平均时间:548.85 ms

     

testForInLoop   测试次数: 100   总时间:86448 ms   平均时间:864.48 ms

     

节点v4.6.1,小对象50属性

     

testObjKeyWhileDecrement   测试次数: 1000   总时间: 4毫秒   平均时间: 0.004毫秒

     

testObjKeyForLoop   测试次数: 1000   总时间: 4毫秒   平均时间: 0.004毫秒

     

testForInLoop   测试次数: 1000   总时间: 14毫秒   平均时间: 0.014毫秒

     

节点v6.9.1,具有大对象10 ^ 6属性

     

testObjKeyWhileDecrement   测试次数: 100   总时间:94252 ms   平均时间:942.52 ms

     

testObjKeyForLoop   测试次数: 100   总时间:92342 ms   平均时间:923.42 ms

     

testForInLoop   测试次数: 100   总时间:72981 ms   平均时间:729.81 ms

     

节点v4.6.1,小对象50属性

     

testObjKeyWhileDecrement   测试次数: 1000   总时间: 8毫秒   平均时间: 0.008毫秒

     

testObjKeyForLoop   测试次数: 1000   总时间: 10毫秒   平均时间: 0.01毫秒

     

testForInLoop   测试次数: 1000   总时间:13 ms   平均时间: 0.013毫秒

以下是我运行的代码:

//Helper functions
function work(value) {
  //do some work on this value
}

function createTestObj(count) {
  var obj = {}
  while (count--) {
    obj["key" + count] = "test";
  }
  return obj;
}

function runOnce(func, obj) {
  var start = Date.now();
  func(obj);
  return Date.now() - start;
}

function testTimer(name, func, obj, count) {
  count = count || 100;
  var times = [];
  var i = count;
  var total;
  var avg;

  while (i--) {
    times.push(runOnce(func, obj));
  }

  total = times.reduce(function (a, b) { return a + b });
  avg = total / count;

  console.log(name);
  console.log('Test Count: ' + count);
  console.log('Total Time: ' + total);
  console.log('Average Time: ' + avg);
  console.log('');
}

//Tests
function testObjKeyWhileDecrement(obj) {
  var keys = Object.keys(obj);
  var i = keys.length;
  while (i--) {
    work(obj[keys[i]]);
  }
}

function testObjKeyForLoop(obj) {
  var keys = Object.keys(obj);
  var len = keys.length;
  var i;
  for (i = 0; i < len; i++) {
    work(obj[keys[i]]);
  }
}

function testForInLoop(obj) {
  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      work(obj[key]);
    }
  }
}

//Run the Tests
var data = createTestObj(50)
testTimer('testObjKeyWhileDecrement', testObjKeyWhileDecrement, data, 1000);
testTimer('testObjKeyForLoop', testObjKeyForLoop, data, 1000);
testTimer('testForInLoop', testForInLoop, data, 1000);

答案 4 :(得分:1)

对于那里的ES6粉丝来说,看起来像

Object.keys(obj).reduce((a,k) => {a += obj[k]; return a}, res)

是迄今为止最快的。

https://jsperf.com/for-in-vs-for-of-keys-vs-keys-reduce

答案 5 :(得分:0)

我今天测试了这个。为了我的目的,获取Object键然后执行一个普通的旧for循环比在循环中执行递减或者for for循环更快。您可以随意更改此模板,以针对您的具体情况测试不同的循环:

&#13;
&#13;
//Helper functions
function work(value) {
  //do some work on this value
}

function createTestObj(count) {
  var obj = {}
  while (count--) {
    obj["key" + count] = "test";
  }
  return obj;
}

//Tests
function test_ObjKeyWhileDecrement(obj) {
  console.log("Time Started: ", new Date().getTime());
  var keys = Object.keys(obj),
    i = keys.length;
  while (i--) {
    work(obj[keys[i]]);
  }
  console.log("Time Finished: ", new Date().getTime());
}

function test_ObjKeyForLoop(obj) {
  console.log("Time Started: ", new Date().getTime());
  for (var i = 0, keys = Object.keys(obj); i < keys.length; i++) {
    work(obj[keys[i]]);
  }
  console.log("Time Finished: ", new Date().getTime());
}

function test_ForInLoop(obj) {
  console.log("Time Started: ", new Date().getTime());
  for (key in obj) {
    work(obj[key]);
  }
  console.log("Time Finished: ", new Date().getTime());
}

//Run the Tests
var data = createTestObj(1000 * 100)
console.log("Test Obj Key While Decrement Loop")
test_ObjKeyWhileDecrement(data);
console.log("Test Obj Key For Loop")
test_ObjKeyForLoop(data);
console.log("Test For In Loop")
test_ForInLoop(data);
&#13;
&#13;
&#13;

您可能希望在实际环境中运行它而不是在jsfiddle中进行测试。尝试多种浏览器。