我正在查看Object.keys
+ forEach
vs 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 的关系。
问题:
jsperf
结果是否相当可观?for-in
方法慢了41%而 Chrome 中的Object.keys
+ forEach
?答案 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)
是迄今为止最快的。
答案 5 :(得分:0)
我今天测试了这个。为了我的目的,获取Object键然后执行一个普通的旧for循环比在循环中执行递减或者for for循环更快。您可以随意更改此模板,以针对您的具体情况测试不同的循环:
//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;
您可能希望在实际环境中运行它而不是在jsfiddle中进行测试。尝试多种浏览器。