¿Cuál es la diferencia entre spread operator y array.concat()
let parts = ['four', 'five']; let numbers = ['one', 'two', 'three']; console.log([...numbers, ...parts]); Array.concat()
let parts = ['four', 'five']; let numbers = ['one', 'two', 'three']; console.log(numbers.concat(parts));Ambos resultados son iguales. Entonces, ¿qué tipo de escenarios queremos utilizar? ¿Y cuál es mejor para el rendimiento?
concat y spreads son muy diferentes cuando el argumento no es una matriz.
Cuando el argumento no es una matriz, concat lo agrega como un todo, mientras que ... intenta iterarlo y falla si no puede. Considerar:
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' ] Aquí, concat trata la cadena atómicamente, mientras que ... usa su iterador predeterminado, char-by-char.
Otro ejemplo:
x = 99; console.log(a.concat(x)); // [1, 2, 3, 99] console.log([...a, ...x]); // TypeError: x is not iterable Nuevamente, para concat el número es un átomo, ... intenta iterarlo y falla.
Por fin:
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 no intenta iterar el generador y lo agrega como un todo, mientras que ... obtiene todos los valores de él.
Para resumir, cuando sus argumentos posiblemente no sean matrices, la elección entre concat y ... depende de si desea que se iteren.
Lo anterior describe el comportamiento predeterminado de concat , sin embargo, ES6 proporciona una forma de anularlo con Symbol.isConcatSpreadable . De forma predeterminada, este símbolo es true para matrices y false para todo lo demás. Establecerlo en true le dice a concat que itere el argumento, al igual que ... lo hace:
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' ] La concat en cuanto al rendimiento es más rápida, probablemente porque puede beneficiarse de las optimizaciones específicas de la matriz, mientras que ... tiene que ajustarse al protocolo de iteración común. Horarios:
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');Bueno console.log(['one', 'two', 'three', 'four', 'five']) también tiene el mismo resultado, entonces, ¿por qué usar cualquiera aquí? :PAG
En general, usaría concat cuando tenga dos (o más) arreglos de fuentes arbitrarias, y usaría la sintaxis extendida en el literal del arreglo si los elementos adicionales que siempre forman parte del arreglo se conocen antes. Entonces, si tuviera una matriz literal con concat en su código, simplemente busque la sintaxis extendida y, de lo contrario, use concat :
[...a, ...b] // bad :-( a.concat(b) // good :-) [x, y].concat(a) // bad :-( [x, y, ...a] // good :-)Además, las dos alternativas se comportan de manera bastante diferente cuando se trata de valores que no son de matriz.
Estoy respondiendo solo a la pregunta de rendimiento ya que ya hay buenas respuestas con respecto a los escenarios. Escribí una prueba y la ejecuté en los navegadores más recientes. Debajo de los resultados y el código.
/* * Performance results. * Browser Spread syntax concat method * -------------------------------------------------- * Chrome 75 626.43ms 235.13ms * Firefox 68 928.40ms 821.30ms * Safari 12 165.44ms 152.04ms * Edge 18 1784.72ms 703.41ms * Opera 62 590.10ms 213.45ms * -------------------------------------------------- */Debajo del código que escribí y usé.
const array1 = []; const array2 = []; const mergeCount = 50; let spreadTime = 0; let concatTime = 0; // Used to popolate the arrays to merge with 10.000.000 elements. for (let i = 0; i < 10000000; ++i) { array1.push(i); array2.push(i); } // The spread syntax performance test. for (let i = 0; i < mergeCount; ++i) { const startTime = performance.now(); const array3 = [ ...array1, ...array2 ]; spreadTime += performance.now() - startTime; } // The concat performance test. for (let i = 0; i < mergeCount; ++i) { const startTime = performance.now(); const array3 = array1.concat(array2); concatTime += performance.now() - startTime; } console.log(spreadTime / mergeCount); console.log(concatTime / mergeCount);