for-in vs Object.keys forEach 没有继承属性
Posted
技术标签:
【中文标题】for-in vs Object.keys forEach 没有继承属性【英文标题】:for-in vs Object.keys forEach without inherited properties 【发布时间】:2014-09-23 00:51:14 【问题描述】:我正在查看Object.keys
+ forEach
与for-in
与普通对象的性能基准。
This 基准测试表明Object.keys
+ forEach
比for-in
方法慢62%。但是,如果您不想获得继承的属性,该怎么办? for-in
包含所有非原生继承对象,所以我们必须使用 hasOwnProperty 来检查。
我试图让另一个benchmark here 做到这一点。但现在for-in
方法比Object.keys
+ forEach
慢41%。
更新
以上测试是在 Chrome 中完成的。再次测试,但使用 Safari,我得到了不同的结果:Object.keys(..).forEach(..) 34% slower
,奇怪。
注意:我进行基准测试的原因是检查 Node.js 的情况。
问题:
Chrome 的jsperf
结果对于 Node.js 是否相当可观?
发生了什么事,为什么单个条件使 for-in
方法比 Chrome 中的 Object.keys
+ forEach
慢 41%?
【问题讨论】:
您能在这里突出显示问题吗?似乎有很多要回答的。 更新了我的问题。很抱歉造成混乱。 【参考方案1】:node.js 使用 V8,虽然我猜它与 Chrome 中的当前版本不一样,但我想它是 node 在该主题上表现的一个很好的指标。
其次,您正在使用forEach
,这在开发时非常方便,但会为每次迭代添加一个回调,这是一项(相对)冗长的任务。所以,如果你对表演感兴趣,为什么不直接使用普通的for
循环呢?
for (var i = 0, keys = Object.keys(object); i < keys.length; i++)
// ...
这会产生您可以获得的最佳性能,也可以解决您在 Safari 中的速度问题。
简而言之:这不是条件,而是对hasOwnProperty
的调用产生了影响。您在每次迭代时都在进行函数调用,这就是 for...in
变慢的原因。
【讨论】:
是的,但我做了第二次测试。表明 forEach 要快得多。考虑 for-in 不应包含继承的属性。 将此 sn-p 添加到基准测试中。它在 2 中排名第一。谢谢。 顺便说一句,在循环的条件部分调用 Object.keys() 并不是一个好主意,因为它将在每次迭代中调用。 @AskarovBeknar 我不知道你是否指的是我上面的 sn-p,但由于那个确切的原因,这不是发生的事情。 啊。是的对不起。它实际上是在初始化部分。那么它是完全正确的。你是对的,使用 for 循环而不是 foreach 会加快执行速度,因为它不会在每次迭代时都创建一个闭包,你可以从中中断。【参考方案2】:请注意:
var keys = Object.keys(obj), i = keys.length;
while(--i)
//
不会为索引 0 运行,然后你会错过你的一个属性。
这在像 ["a","b","c","d"] 这样的数组中只会运行 d,c,b,你会错过 "a" 因为索引是 0 和 0是假的。
需要在while检查后递减:
var keys = Object.keys(obj), i = keys.length;
while(i--)
//
【讨论】:
【参考方案3】:我今天也对此感兴趣,但主要是因为我不喜欢在我已经知道我的对象是干净的(因为它们是从对象文字创建的)时必须使用 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 年末配备 16GB 内存和 2.4Ghz i5 处理器的 MacBook Pro 上运行的。在测试期间,每个测试都与单个 cpu 的 100% 挂钩,平均 rss 约为 500MB,峰值为 1GB 的 rss。希望这对某人有所帮助。
这是我针对具有大对象(10^6 个属性)和小对象(50 个属性)的 nodejs v6.9.1 和 v4.6.1 运行的结果
具有大对象 10^6 属性的节点 v4.6.1
testObjKeyWhileDecrement 测试次数: 100 总时间: 57595 毫秒 平均时间: 575.95 毫秒
testObjKeyForLoop 测试次数: 100 总时间: 54885 毫秒 平均时间: 548.85 毫秒
testForInLoop 测试次数: 100 总时间: 86448 毫秒 平均时间: 864.48 毫秒
具有小对象 50 个属性的节点 v4.6.1
testObjKeyWhileDecrement 测试计数: 1000 总时间: 4 毫秒 平均时间: 0.004 毫秒
testObjKeyForLoop 测试计数: 1000 总时间: 4 毫秒 平均时间: 0.004 毫秒
testForInLoop 测试计数: 1000 总时间: 14 毫秒 平均时间: 0.014 毫秒
具有大对象 10^6 属性的节点 v6.9.1
testObjKeyWhileDecrement 测试次数: 100 总时间: 94252 毫秒 平均时间: 942.52 毫秒
testObjKeyForLoop 测试次数: 100 总时间: 92342 毫秒 平均时间: 923.42 毫秒
testForInLoop 测试次数: 100 总时间: 72981 毫秒 平均时间: 729.81 毫秒
具有小对象 50 个属性的节点 v4.6.1
testObjKeyWhileDecrement 测试计数: 1000 总时间: 8 毫秒 平均时间: 0.008 毫秒
testObjKeyForLoop 测试计数: 1000 总时间: 10 毫秒 平均时间: 0.01 毫秒
testForInLoop 测试计数: 1000 总时间: 13 毫秒 平均时间: 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);
【讨论】:
我刚刚测试了 Object.keys(obj).forEach(work),它正好适合 forIn 和 Object.keys 之间的数字 for 和 while 循环。 我在这里整理了一个 jsperf:jsperf.com/object-property-iteration【参考方案4】:对于那些仍然关心在 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 循环与长度上限比较快很多。
【讨论】:
预减量检查会破坏索引0
处的条件。见答案***.com/a/35117591/303016【参考方案5】:
我今天测试了这个。就我的目的而言,获取 Object 键然后执行普通的旧 for 循环比执行递减的 while 或 for in 循环要快。随意更改此模板以针对您的个人情况测试不同的循环:
//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);
您可能希望在实际环境中运行它来测试,而不是在 jsfiddle 中。也尝试多个浏览器。
【讨论】:
你应该使用console.time
和console.timeEnd
来处理这种情况。【参考方案6】:
对于 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
【讨论】:
2018:keys-reduce 在 V8 上仅快 0.5%,但在 FireFox 上慢 2%。以上是关于for-in vs Object.keys forEach 没有继承属性的主要内容,如果未能解决你的问题,请参考以下文章
javascript中 for in for forEach for of Object.keys().
JavaScript中in操作符(for..in)Object.keys()和Object.getOwnPropertyNames()的区别
JavaScript中in操作符(for..in)Object.keys()和Object.getOwnPropertyNames()的区别
[转] JavaScript中in操作符(for..in)Object.keys()和Object.getOwnPropertyNames()的区别