𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀
Para los hechos, se realiza una prueba de rendimiento en jsperf y se comprueban algunas cosas en la consola. Para la investigación, se utiliza el sitio web irt.org . A continuación se muestra una colección de todas estas fuentes juntas más una función de ejemplo en la parte inferior.
╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Método ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / Sec. ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Matrices dispersas ║SÍ! ║Solo las rebanadas ║ no ║ Quizás 2 ║no ║
║ mantenido escaso ║ ║ matriz (1er argumento) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Soporte ║MSIE 4║MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║Edge 12 ║
║ ( fuente ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Actos de tipo matriz ║no ║Solo los empujados ║ ¡SÍ! ║ SI! ║Si tengo ║
║ como una matriz ║ ║ matriz (2º argumento) ║ ║ ║iterador 1 ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Si el objeto tipo matriz no tiene una propiedad Symbol.iterator , intente
difundirlo arrojará una excepción.
2 Depende del código. El siguiente código de ejemplo "SÍ" conserva la escasez.
function mergeCopyTogether(inputOne, inputTwo){
var oneLen = inputOne.length, twoLen = inputTwo.length;
var newArr = [], newLen = newArr.length = oneLen + twoLen;
for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {
tmp = inputOne[i];
if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
}
for (var two=0; i !== newLen; ++i, ++two) {
tmp = inputTwo[two];
if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;
}
return newArr;
}
Como se vio anteriormente, diría que Concat es casi siempre el camino a seguir para el rendimiento y la capacidad de retener la escasez de matrices de repuesto. Luego, para los me gusta de la matriz (como las listas DOMNodeLists document.body.children
), recomendaría usar el bucle for porque es el segundo método más eficaz y el único otro método que retiene las matrices dispersas. A continuación, repasaremos rápidamente qué se entiende por matrices dispersas y me gusta de matrices para aclarar la confusión.
𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲
Al principio, algunas personas pueden pensar que esto es una casualidad y que los proveedores de navegadores eventualmente llegarán a optimizar Array.prototype.push para ser lo suficientemente rápido como para vencer a Array.prototype.concat. ¡INCORRECTO! Array.prototype.concat siempre será más rápido (al menos en principio) porque es un simple copiar y pegar sobre los datos. A continuación, se muestra un diagrama visual simplificado de cómo se vería una implementación de matriz de 32 bits (tenga en cuenta que las implementaciones reales son MUCHO más complicadas)
Byte ║ Datos aquí
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int longitud = 0x00000001
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueIndex = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (o donde esté en la memoria)
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
Como se ve arriba, todo lo que necesita hacer para copiar algo así es casi tan simple como copiarlo byte por byte. Con Array.prototype.push.apply, es mucho más que un simple copiar y pegar sobre los datos. El ".apply" tiene que verificar cada índice en la matriz y convertirlo en un conjunto de argumentos antes de pasarlo a Array.prototype.push. Luego, Array.prototype.push tiene que asignar adicionalmente más memoria cada vez, y (para algunas implementaciones de navegador) incluso puede recalcular algunos datos de búsqueda de posición por escasez.
Una forma alternativa de pensar es esto. La matriz de origen es una gran pila de papeles grapados entre sí. La matriz de origen dos también es otra gran pila de papeles. ¿Sería más rápido para ti
- Vaya a la tienda, compre suficiente papel para una copia de cada fuente. Luego, coloque cada pila de papel de la matriz de origen a través de una máquina copiadora y engrape las dos copias resultantes juntas.
- Vaya a la tienda, compre suficiente papel para una sola copia de la primera matriz fuente. Luego, copie la matriz de origen en el nuevo papel a mano, asegurándose de llenar cualquier espacio escaso en blanco. Luego, regrese a la tienda, compre suficiente papel para la segunda matriz fuente. Luego, revise la segunda matriz de origen y cópiela sin asegurarse de que no haya espacios en blanco en la copia. Luego, engrape todos los papeles copiados.
En la analogía anterior, la opción # 1 representa Array.prototype.concat mientras que # 2 representa Array.prototype.push.apply. Probemos esto con un JSperf similar que solo difiere en que este prueba los métodos sobre matrices dispersas, no sobre matrices sólidas. Uno puede encontrarlo aquí mismo .
Por lo tanto, descarto mi caso de que el futuro del rendimiento para este caso de uso en particular no radica en Array.prototype.push, sino en Array.prototype.concat.
𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀
𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀
Cuando ciertos miembros de la matriz simplemente faltan. Por ejemplo:
// This is just as an example. In actual code,
// do not mix different types like this.
var mySparseArray = [];
mySparseArray[0] = "foo";
mySparseArray[10] = undefined;
mySparseArray[11] = {};
mySparseArray[12] = 10;
mySparseArray[17] = "bar";
console.log("Length: ", mySparseArray.length);
console.log("0 in it: ", 0 in mySparseArray);
console.log("arr[0]: ", mySparseArray[0]);
console.log("10 in it: ", 10 in mySparseArray);
console.log("arr[10] ", mySparseArray[10]);
console.log("20 in it: ", 20 in mySparseArray);
console.log("arr[20]: ", mySparseArray[20]);
Alternativamente, javascript le permite inicializar matrices de repuesto fácilmente.
var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];
𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀
Un tipo de matriz es un objeto que tiene al menos una length
propiedad, pero no se inicializó con new Array
o []
; Por ejemplo, los siguientes objetos se clasifican como tipo matriz.
{0: "foo", 1: "bar", longitud: 2}
document.body.children
nuevo Uint8Array (3)
- Esto es similar a una matriz porque, aunque es una matriz (n) (tipificada), coaccionarla a una matriz cambia el constructor.
(función () {argumentos de retorno}) ()
Observe lo que sucede usando un método que coacciona los me gusta de la matriz en matrices como el segmento.
var slice = Array.prototype.slice;
// For arrays:
console.log(slice.call(["not an array-like, rather a real array"]));
// For array-likes:
console.log(slice.call({0: "foo", 1: "bar", length:2}));
console.log(slice.call(document.body.children));
console.log(slice.call(new Uint8Array(3)));
console.log(slice.call( function(){return arguments}() ));
- NOTA: Es una mala práctica llamar a argumentos de corte en función debido al rendimiento.
Observe lo que sucede usando un método que no coacciona los me gusta de la matriz en matrices como concat.
var empty = [];
// For arrays:
console.log(empty.concat(["not an array-like, rather a real array"]));
// For array-likes:
console.log(empty.concat({0: "foo", 1: "bar", length:2}));
console.log(empty.concat(document.body.children));
console.log(empty.concat(new Uint8Array(3)));
console.log(empty.concat( function(){return arguments}() ));