spread operator vs array.concat() spread operator vs array.concat() arrays arrays

spread operator vs array.concat()


concat and spreads are very different when the argument is not an array.

When the argument is not an array, concat adds it as a whole, while ... tries to iterate it and fails if it can't. Consider:

a = [1, 2, 3]x = 'hello';console.log(a.concat(x));  // [ 1, 2, 3, 'hello' ]console.log([...a, ...x]); // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ]

Here, concat treats the string atomically, while ... uses its default iterator, char-by-char.

Another example:

x = 99;console.log(a.concat(x));   // [1, 2, 3, 99]console.log([...a, ...x]);  // TypeError: x is not iterable

Again, for concat the number is an atom, ... tries to iterate it and fails.

Finally:

function* gen() { yield *'abc' }console.log(a.concat(gen()));   // [ 1, 2, 3, Object [Generator] {} ]console.log([...a, ...gen()]);  // [ 1, 2, 3, 'a', 'b', 'c' ]

concat makes no attempt to iterate the generator and appends it as a whole, while ... nicely fetches all values from it.

To sum it up, when your arguments are possibly non-arrays, the choice between concat and ... depends on whether you want them to be iterated.

The above describes the default behaviour of concat, however, ES6 provides a way to override it with Symbol.isConcatSpreadable. By default, this symbol is true for arrays, and false for everything else. Setting it to true tells concat to iterate the argument, just like ... does:

str = 'hello'console.log([1,2,3].concat(str)) // [1,2,3, 'hello']str = new String('hello');str[Symbol.isConcatSpreadable] = true;console.log([1,2,3].concat(str)) // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ]

Performance-wise concat is faster, probably because it can benefit from array-specific optimizations, while ... has to conform to the common iteration protocol. Timings:

let big = (new Array(1e5)).fill(99);let i, x;console.time('concat-big');for(i = 0; i < 1e2; i++) x = [].concat(big)console.timeEnd('concat-big');console.time('spread-big');for(i = 0; i < 1e2; i++) x = [...big]console.timeEnd('spread-big');let a = (new Array(1e3)).fill(99);let b = (new Array(1e3)).fill(99);let c = (new Array(1e3)).fill(99);let d = (new Array(1e3)).fill(99);console.time('concat-many');for(i = 0; i < 1e2; i++) x = [1,2,3].concat(a, b, c, d)console.timeEnd('concat-many');console.time('spread-many');for(i = 0; i < 1e2; i++) x = [1,2,3, ...a, ...b, ...c, ...d]console.timeEnd('spread-many');