I was interested in this today as well, but mostly because I don't like having to test with hasOwnProperty to pass the default lint when I already know my objects are clean (as they've been created from object literals). Anyway, I expanded a little on the answer by @styonsk to include a better output and to run multiple tests and return the output.
Conclusion: It's complicated for node. The best time looks like using Object.keys() with either a numerical for loop or a while loop on nodejs v4.6.1. On v4.6.1, the forIn loop with hasOwnProperty is the slowest method. However, on node v6.9.1 it is the fastest, but it is still slower than both Object.keys() iterators on v4.6.1.
Notes: This was run on a late 2013 MacBook Pro with 16GB ram and a 2.4Ghz i5 processor. Every test pegged 100% of a single cpu for the duration of the test and had an average rss of about 500MB and peaked at 1GB of rss. Hope this helps someone.
Here are my results running against nodejs v6.9.1 and v4.6.1 with large objects (10^6 properties) and small objects(50 properties)
Node v4.6.1 with large object 10^6 properties
testObjKeyWhileDecrement
Test Count: 100
Total Time: 57595 ms
Average Time: 575.95 ms
testObjKeyForLoop
Test Count: 100
Total Time: 54885 ms
Average Time: 548.85 ms
testForInLoop
Test Count: 100
Total Time: 86448 ms
Average Time: 864.48 ms
Node v4.6.1 with small object 50 properties
testObjKeyWhileDecrement
Test Count: 1000
Total Time: 4 ms
Average Time: 0.004 ms
testObjKeyForLoop
Test Count: 1000
Total Time: 4 ms
Average Time: 0.004 ms
testForInLoop
Test Count: 1000
Total Time: 14 ms
Average Time: 0.014 ms
Node v6.9.1 with large object 10^6 properties
testObjKeyWhileDecrement
Test Count: 100
Total Time: 94252 ms
Average Time: 942.52 ms
testObjKeyForLoop
Test Count: 100
Total Time: 92342 ms
Average Time: 923.42 ms
testForInLoop
Test Count: 100
Total Time: 72981 ms
Average Time: 729.81 ms
Node v4.6.1 with small object 50 properties
testObjKeyWhileDecrement
Test Count: 1000
Total Time: 8 ms
Average Time: 0.008 ms
testObjKeyForLoop
Test Count: 1000
Total Time: 10 ms
Average Time: 0.01 ms
testForInLoop
Test Count: 1000
Total Time: 13 ms
Average Time: 0.013 ms
And following is the code I ran:
//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);