Reputation: 5723
I tested various array concatenation techniques not because it actually matters to my code but merely as an aside, to see where we are right now. As expected the very new ES 2015 spread operator is beaten by the old concat()
method on JavaScript arrays by a considerable margin.
However, what surprised me a bit was when I compared these two:
var a = b = c = [1,2,3,4,5];
// SLOWER (V8 and Edge, very slightly faster in Firefox)
console.time('t1');
for (i = 0; i < 1000000; i++) {
Array.prototype.concat(a, b, c);
};
console.timeEnd('t1')
// FASTER (V8 and Edge, very slightly slower in Firefox)
console.time('t2');
for (i = 0; i < 1000000; i++) {
[].concat(a, b, c);
};
console.timeEnd('t2')
I tested, and ran multiple times before doing the next one, on the latest Node.js, Chrome and Edge browsers. With V8 (Node.js, Chrome) the result is even bigger, but even for Edge the first option is always clearly - on V8 significantly - slower than the second option. In Firefox results are reversed but almost equal, so let's limit the question to the other two browser engines (V8 and Chakra).
So I'm asking merely out of curiosity, since I did not foresee this at all,
1) Apart from performance, is there any practical difference between the two ways to concatenate those arrays?
2) Why is the one that AFAICS creates one object (array) less than the other (the initial array) slower?
I know the methods are the same, that's why I tested the direct access to the method on the prototype instead of creating an (unused) array object to access it. I also know that it's browser dependent, which is a) why I tested two V8 based systems (Chrome and Node.js) and the Microsoft Edge browser, and b) why I included the runnable test case above.
Upvotes: 6
Views: 4340
Reputation: 34667
The accepted answer is inaccurate. In Chromium the code below shows that the difference in execution is not related to outside lookups, but to the context, i.e. this
inside concat()
. (In Firefox, the advantage of [].concat()
is marginal and not even consistent.)
In the [].concat()
case the context is an empty array, whose elements will be included in the concatenation result. With Array.prototype.concat()
the context is Array.prototype
, which is also an array, also “empty” in the “array-sense”, but full with methods... I’m not sure why this makes such a difference.
Update: I was playing around with a custom made array: one with many non-number members, i.e. length === 0. It took twice the time compared to the empty array, but still not as slow as Array.prototype
...
const a = b = c = [1,2,3,4,5];
const ap = Array.prototype;
const apc = Array.prototype.concat;
const array = [];
function loopAndLog(msg, context) {
console.time(msg);
for (let i = 0; i < 1000000; i++) {
apc.call(context, a, b, c);
};
console.timeEnd(msg)
}
loopAndLog('[].concat() equivalent first round', array);
loopAndLog('Array.prototype.concat() equivalent first round', ap);
loopAndLog('[].concat() equivalent second round', array);
loopAndLog('Array.prototype.concat() equivalent second round', ap);
Upvotes: 0
Reputation: 7257
Array.prototype.concat
needs to be resolved in every loop. If you would lookup the function only once, you'll see different results. This may vary depending on the implementation of the runtime though.
var a = b = c = [1,2,3,4,5];
// Array.prototype.concat
console.time('t1');
var apc = Array.prototype.concat;
for (i = 0; i < 1000000; i++) {
apc.call([], a, b, c);
};
console.timeEnd('t1')
// [].concat
console.time('t2');
for (i = 0; i < 1000000; i++) {
[].concat(a, b, c);
};
console.timeEnd('t2')
// They're the same:
console.log(Array.prototype.concat === [].concat);
To get more accurate results, use a proper benchmarking library (eliminates warm up time for example).
Upvotes: 11