𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀
Per i fatti, viene eseguito un test delle prestazioni su jsperf e il controllo di alcune cose nella console. Per la ricerca, viene utilizzato il sito Web irt.org . Di seguito una raccolta di tutte queste fonti messe insieme più una funzione di esempio in fondo.
╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Metodo oncConcetti║slice & push.apply ║ push.apply x2 ║ ForLoop ║Scover ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / sec ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Matrici sparse ║ SÌ! ║Solo affettato ║ no ║ Forse 2 ║no ║
║ tenuto scarso ║array (1 ° arg) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Supporto ║MSIE 4║MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║Edge 12 ║
║ ( fonte ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Atti simili ad array║no ║Solo la spinta ║ SÌ! ║ SÌ! FSe hai ║
║come un array ║ ║array (2nd arg) ║ ║ ║iteratore 1 ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Se l'oggetto simile a un array non ha una proprietà Symbol.iterator , provare
diffonderlo genererà un'eccezione.
2 Dipende dal codice. Il seguente codice di esempio "SÌ" preserva la scarsità.
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;
}
Come visto sopra, direi che Concat è quasi sempre la strada da percorrere sia per le prestazioni che per la capacità di conservare la scarsità di array di riserva. Quindi, per gli array-like (come DOMNodeLists come document.body.children
), consiglierei di usare il ciclo for perché è sia il secondo più performante sia l'unico altro metodo che conserva gli array sparsi. Di seguito, analizzeremo rapidamente cosa si intende per array sparsi e simili a array per chiarire la confusione.
𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲
Inizialmente, alcune persone potrebbero pensare che si tratti di un colpo di fortuna e che i venditori di browser alla fine si adopereranno per ottimizzare Array.prototype.push per essere abbastanza veloci da battere Array.prototype.concat. SBAGLIATO! Array.prototype.concat sarà sempre più veloce (almeno in linea di principio) perché è una semplice copia-incolla sui dati. Di seguito è riportato un diagramma persuado-visivo semplificato di come potrebbe apparire un'implementazione di array a 32 bit (si noti che le implementazioni reali sono MOLTO più complicate)
Byte ║ Dati qui
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ int length = 0x00000001
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ int valueIndex = 0x00000000
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (o ovunque sia in memoria)
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
Come visto sopra, tutto ciò che devi fare per copiare qualcosa del genere è quasi semplice come copiarlo byte per byte. Con Array.prototype.push.apply, è molto più di una semplice copia-incolla sui dati. ".Apply" deve controllare ciascun indice dell'array e convertirlo in una serie di argomenti prima di passarlo a Array.prototype.push. Quindi, Array.prototype.push deve allocare ulteriore memoria ogni volta e (per alcune implementazioni del browser) potrebbe persino ricalcolare alcuni dati di ricerca della posizione per scarsità.
Un modo alternativo di pensarci è questo. L'array di origine uno è una grande pila di fogli pinzati insieme. L'array di origine due è anche un'altra grande pila di fogli. Sarebbe più veloce per te
- Vai al negozio, acquista abbastanza carta necessaria per una copia di ciascun array di origine. Quindi inserire ciascuna matrice di carta pile di carta attraverso una fotocopiatrice e pinzare insieme le due copie risultanti.
- Vai al negozio, acquista abbastanza carta per una singola copia del primo array di origine. Quindi, copiare manualmente l'array di origine sul nuovo foglio, assicurandosi di riempire eventuali spazi vuoti. Quindi, torna al negozio, acquista abbastanza carta per il secondo array di origine. Quindi, passare attraverso il secondo array di origine e copiarlo senza garantire spazi vuoti nella copia. Quindi, pinzare insieme tutti i documenti copiati.
Nell'analogia sopra, l'opzione 1 rappresenta Array.prototype.concat mentre # 2 rappresenta Array.prototype.push.apply. Proviamo questo con un JSperf simile che differisce solo per il fatto che questo testa i metodi su array sparsi, non su array solidi. Lo si può trovare proprio qui .
Pertanto, appoggio il caso che il futuro delle prestazioni per questo particolare caso d'uso non risieda in Array.prototype.push, ma piuttosto in Array.prototype.concat.
𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀
𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀
Quando alcuni membri dell'array mancano semplicemente. Per esempio:
// 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]);
In alternativa, JavaScript consente di inizializzare facilmente array di riserva.
var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];
𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀
Un array-like è un oggetto che ha almeno una length
proprietà, ma non è stato inizializzato con new Array
o []
; Ad esempio, gli oggetti seguenti sono classificati come array.
{0: "foo", 1: "bar", lunghezza: 2}
document.body.children
nuovo Uint8Array (3)
- È simile ad un array perché, sebbene sia un array (n) (tipizzato), il suo inserimento in un array modifica il costruttore.
(function () {return argomenti}) ()
Osserva cosa succede usando un metodo che costringe i array-like in array come slice.
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: è una cattiva pratica chiamare slice sugli argomenti della funzione a causa delle prestazioni.
Osservare ciò che accade utilizzando un metodo che non obbliga gli array-like in array come 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}() ));