𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀
Pour les faits, un test de performance sur jsperf et une vérification de certaines choses dans la console sont effectués. Pour la recherche, le site Internet irt.org est utilisé. Vous trouverez ci-dessous une collection de toutes ces sources ainsi qu'un exemple de fonction en bas.
╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Méthode ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / sec ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Tableaux clairsemés ║OUI! ║Seulement les tranches ║ non ║ Peut-être 2 ║non ║
║ clairsemé ║ rayarray (1er argument) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Prise en charge ║MSIE 4║MSIE 5.5 IE MSIE 5.5 IE MSIE 4 ║Edge 12 ║
║ ( source ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Activités de type tableau║non ║Seulement le poussé ║ OUI! ║ OUI! HaveSi have
║comme un tableau ║ rayarray (2nd arg) ║ ║ ║iterator 1 ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Si l'objet de type tableau n'a pas de propriété Symbol.iterator , essayez
pour le diffuser, il lèvera une exception.
2 Dépend du code. L'exemple de code suivant "OUI" préserve la parcimonie.
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;
}
Comme vu ci-dessus, je dirais que Concat est presque toujours la voie à suivre pour les performances et la capacité à conserver la rareté des tableaux de rechange. Ensuite, pour les tableaux comme (comme DOMNodeLists comme document.body.children
), je recommanderais d'utiliser la boucle for car c'est à la fois la 2e méthode la plus performante et la seule autre méthode qui conserve des tableaux clairsemés. Ci-dessous, nous passerons rapidement en revue ce que l'on entend par tableaux clairsemés et tableaux similaires pour dissiper la confusion.
𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲
Au début, certaines personnes peuvent penser qu'il s'agit d'un hasard et que les fournisseurs de navigateurs finiront par optimiser l'optimisation d'Array.prototype.push pour être suffisamment rapide pour battre Array.prototype.concat. FAUX! Array.prototype.concat sera toujours plus rapide (en principe au moins) car il s'agit d'un simple copier-coller sur les données. Vous trouverez ci-dessous un diagramme persuado-visuel simplifié de ce à quoi pourrait ressembler une implémentation de tableau 32 bits (veuillez noter que les implémentations réelles sont BEAUCOUP plus compliquées)
Octet ║ Données ici
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 length longueur int = 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 (ou partout où il est en mémoire)
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
Comme vu ci-dessus, tout ce que vous devez faire pour copier quelque chose comme ça est presque aussi simple que de le copier octet par octet. Avec Array.prototype.push.apply, c'est bien plus qu'un simple copier-coller sur les données. Le ".apply" doit vérifier chaque index du tableau et le convertir en un ensemble d'arguments avant de le passer à Array.prototype.push. Ensuite, Array.prototype.push doit en outre allouer plus de mémoire à chaque fois, et (pour certaines implémentations de navigateur), peut-être même recalculer certaines données de recherche de position pour la rareté.
Une autre façon d'y penser est la suivante. Le tableau source est une grande pile de papiers agrafés ensemble. Le tableau source deux est également une autre grande pile de papiers. Serait-il plus rapide pour vous de
- Allez au magasin, achetez suffisamment de papier pour une copie de chaque tableau source. Ensuite, placez chaque pile source de papier dans une photocopieuse et agrafez les deux copies résultantes ensemble.
- Allez au magasin, achetez suffisamment de papier pour une seule copie de la première matrice source. Ensuite, copiez le tableau source sur le nouveau papier à la main, en veillant à remplir tous les points clairsemés vides. Ensuite, retournez au magasin, achetez suffisamment de papier pour la deuxième matrice source. Ensuite, parcourez le deuxième tableau source et copiez-le tout en vous assurant qu'il n'y a pas d'espace vide dans la copie. Ensuite, agrafez tous les papiers copiés ensemble.
Dans l'analogie ci-dessus, l'option # 1 représente Array.prototype.concat tandis que # 2 représente Array.prototype.push.apply. Essayons cela avec un JSperf similaire qui ne diffère que par le fait que celui-ci teste les méthodes sur des tableaux clairsemés, pas des tableaux solides. On peut le trouver ici .
Par conséquent, je me repose sur le fait que l'avenir des performances pour ce cas d'utilisation particulier ne réside pas dans Array.prototype.push, mais plutôt dans Array.prototype.concat.
𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀
𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀
Lorsque certains membres du tableau sont simplement manquants. Par exemple:
// 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]);
Alternativement, javascript vous permet d'initialiser facilement des tableaux de rechange.
var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];
𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀
Un tableau-comme est un objet qui a au moins une length
propriété, mais n'a pas été initialisé avec new Array
ou []
; Par exemple, les objets ci-dessous sont classés comme des tableaux.
{0: "foo", 1: "bar", longueur: 2}
document.body.children
nouveau Uint8Array (3)
- Cela ressemble à un tableau car bien qu'il s'agisse d'un tableau (n) (typé), le contraindre à un tableau change le constructeur.
(fonction () {retourner les arguments}) ()
Observez ce qui se passe en utilisant une méthode qui contraint les tableaux comme des tableaux comme des tranches.
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}() ));
- REMARQUE: il est incorrect d'appeler slice sur les arguments de fonction en raison des performances.
Observez ce qui se passe en utilisant une méthode qui ne contraint pas les tableaux comme dans des tableaux comme 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}() ));