Le moyen le plus rapide de convertir JavaScript NodeList en tableau?


251

Les questions précédemment répondues ici indiquaient que c'était le moyen le plus rapide:

//nl is a NodeList
var arr = Array.prototype.slice.call(nl);

En comparant sur mon navigateur, j'ai constaté qu'il est plus de 3 fois plus lent que cela:

var arr = [];
for(var i = 0, n; n = nl[i]; ++i) arr.push(n);

Ils produisent tous deux la même sortie, mais j'ai du mal à croire que ma deuxième version est le moyen le plus rapide possible, d'autant plus que les gens ont dit le contraire ici.

Est-ce une bizarrerie dans mon navigateur (Chromium 6)? Ou existe-t-il un moyen plus rapide?

EDIT: Pour tous ceux qui s'en soucient, je me suis installé sur ce qui suit (qui semble être le plus rapide dans tous les navigateurs que j'ai testés):

//nl is a NodeList
var l = []; // Will hold the array of Node's
for(var i = 0, ll = nl.length; i != ll; l.push(nl[i++]));

EDIT2: J'ai trouvé un moyen encore plus rapide

// nl is the nodelist
var arr = [];
for(var i = nl.length; i--; arr.unshift(nl[i]));

2
arr[arr.length] = nl[i];peut être plus rapide que arr.push(nl[i]);cela car il évite un appel de fonction.
Luc125

9
Cette page jsPerf garde une trace de toutes les réponses sur cette page: jsperf.com/nodelist-to-array/27
pilau

Veuillez noter que "EDIT2: J'ai trouvé un moyen plus rapide" est 92% plus lent sur IE8.
Camilo Martin

2
Puisque vous savez déjà combien de nœuds vous avez:var i = nl.length, arr = new Array(i); for(; i--; arr[i] = nl[i]);
mems

@ Luc125 Cela dépend du navigateur, puisque l'implémentation push peut être optimisée, je pense au chrome car la v8 est bonne avec ce genre de choses.
axelduch

Réponses:


197

Le second a tendance à être plus rapide dans certains navigateurs, mais l'essentiel est que vous devez l'utiliser car le premier n'est tout simplement pas multi-navigateur. Même si les temps sont changeants

@kangax ( aperçu IE 9 )

Array.prototype.slice peut désormais convertir certains objets hôtes (par exemple NodeList) en tableaux - ce que la majorité des navigateurs modernes ont pu faire pendant un bon moment.

Exemple:

Array.prototype.slice.call(document.childNodes);

??? Les deux sont compatibles avec tous les navigateurs - Javascript (au moins s'il prétend être compatible avec la spécification ECMAscript) est Javascript; Array, prototype, slice et call sont toutes des fonctionnalités du langage de base + types d'objet.
Jason S

6
mais ils ne peuvent pas être utilisés sur NodeLists dans IE (je sais que ça craint, mais bon voir ma mise à jour)
gblazex

9
parce que les NodeLists ne font pas partie du langage, ils font partie de l'API DOM, qui est connue pour être boguée / imprévisible, en particulier dans IE
gblazex

3
Array.prototype.slice n'est pas multi-navigateur, si vous prenez en compte IE8.
Lajos Meszaros

1
Oui, c'est essentiellement ma réponse :) Bien que ce soit plus pertinent en 2010 qu'aujourd'hui (2015).
gblazex

224

Avec ES6, nous avons maintenant un moyen simple de créer un tableau à partir d'une NodeList: la Array.from()fonction.

// nl is a NodeList
let myArray = Array.from(nl)

comment cette nouvelle méthode ES6 se compare-t-elle en vitesse aux autres qui ont été mentionnées ci-dessus?
user5508297

10
@ user5508297 Mieux que l'astuce d'appel de tranche. Plus lent que les boucles for, mais ce n'est pas exactement que nous pouvons vouloir avoir un tableau sans le traverser. Et la syntaxe est belle, simple et facile à retenir!
webdif

Une bonne chose à propos d'Array.from est que vous pouvez utiliser l'argument de la carte:console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]
honzajde


19

Quelques optimisations:

  • enregistrer la longueur de la NodeList dans une variable
  • définissez explicitement la longueur du nouveau tableau avant de définir.
  • accéder aux indices, plutôt que de pousser ou de déplacer.

Code ( jsPerf ):

var arr = [];
for (var i = 0, ref = arr.length = nl.length; i < ref; i++) {
 arr[i] = nl[i];
}

Vous pouvez gagner un peu plus de temps en utilisant Array (length) plutôt qu'en créant le tableau et en le dimensionnant séparément. Si vous utilisez ensuite const pour déclarer le tableau et sa longueur, avec un let à l'intérieur de la boucle, il se termine environ 1,5% plus rapidement que la méthode ci-dessus: const a = Array (nl.length), c = a.length; pour (soit b = 0; b <c; b ++) {a [b] = nl [b]; } voir jsperf.com/nodelist-to-array/93
BrianFreud

15

Les résultats dépendront complètement du navigateur, pour donner un verdict objectif, nous devons faire des tests de performances, voici quelques résultats, vous pouvez les exécuter ici :

Chrome 6:

Firefox 3.6:

Firefox 4.0b2:

Safari 5:

Aperçu de la plateforme IE9 3:


1
Je me demande comment l'inverse de la boucle résiste à ces ... for (var i=o.length; i--;)... la "boucle" de ces tests a-t-elle réévalué la propriété length à chaque itération?
Dagg Nabbit

14

Le navigateur le plus rapide et le plus croisé est

for(var i=-1,l=nl.length;++i!==l;arr[i]=nl[i]);

Comme je l'ai comparé dans

http://jsbin.com/oqeda/98/edit

* Merci @CMS pour l'idée!

Chrome (similaire à Google Chrome) Firefox Opéra


1
le lien semble être faux, devrait être 91, au lieu de 89 pour inclure le test que vous mentionnez. Et 98 semble le plus complet.
Yaroslav Yakovlev

9

Dans ES6, vous pouvez utiliser:

  • Array.from

    let array = Array.from(nodelist)

  • Opérateur de diffusion

    let array = [...nodelist]


N'ajoute rien de nouveau à ce qui a déjà été écrit dans les réponses précédentes.
Stefan Becker

7
NodeList.prototype.forEach = Array.prototype.forEach;

Vous pouvez maintenant faire document.querySelectorAll ('div'). ForEach (function () ...)


Bonne idée, merci @John! Cependant, NodeListne fonctionne pas mais Objectest: Object.prototype.forEach = Array.prototype.forEach; document.getElementsByTagName("img").forEach(function(img) { alert(img.src); });
Ian Campbell

3
N'utilisez pas Object.prototype: il casse JQuery et une tonne de choses comme la syntaxe littérale du dictionnaire.
Nate Symer

Bien sûr, évitez d'étendre les fonctions natives intégrées.
roland

5

plus rapide et plus court:

// nl is the nodelist
var a=[], l=nl.length>>>0;
for( ; l--; a[l]=nl[l] );

3
Pourquoi >>>0? Et pourquoi ne pas mettre les affectations sur la première partie de la boucle for?
Camilo Martin

5
C'est aussi un buggy. Quand lest 0, la boucle se terminera, donc le 0e élément ne sera pas copié (rappelez-vous qu'il y a un élément à l'index 0)
Camilo Martin

1
J'adore cette réponse, mais ... Tous ceux qui se demandent: le >>> peut ne pas être nécessaire ici mais est utilisé pour garantir que la longueur de la liste de nodules adhère aux spécifications du tableau; il garantit qu'il s'agit d'un entier 32 bits non signé. Vérifiez-le ici ecma-international.org/ecma-262/5.1/#sec-15.4 Si vous aimez le code illisible, utilisez cette méthode avec les suggestions de @ CamiloMartin!
Todd

En réponse à @CamiloMartin - Il est risqué de mettre varà l'intérieur la première partie d'une forboucle à cause du «levage variable». La déclaration de varsera «hissée» en haut de la fonction, même si la varligne apparaît quelque part plus bas, ce qui peut provoquer des effets secondaires qui ne sont pas évidents dans la séquence de code. Par exemple un code dans la même fonction se produisant avant la boucle pourrait dépendre de aet létant non déclarés. Par conséquent, pour une meilleure prévisibilité, déclarez vos vars en haut de la fonction (ou si sur ES6, utilisez constou à la letplace, qui ne sont pas hissés).
brennanyoung

3

Consultez cet article de blog ici qui parle de la même chose. D'après ce que je comprends, le temps supplémentaire pourrait avoir à voir avec la montée de la chaîne de portée.


Intéressant. Je viens de faire des tests similaires maintenant et Firefox 3.6.3 ne montre aucune augmentation de la vitesse dans les deux sens, tandis qu'Opera 10.6 a une augmentation de 20% et Chrome 6 a une augmentation de 230% (!) En le faisant de manière itérative manuelle.
jairajs89

@ jairajs89 assez étrange. Il semble que le Array.prototype.slicesoit dépendant du navigateur. Je me demande quel algorithme chacun des navigateurs utilise.
Vivin Paliath

3

C'est la fonction que j'utilise dans mon JS:

function toArray(nl) {
    for(var a=[], l=nl.length; l--; a[l]=nl[l]);
    return a;
}

1

Voici les graphiques mis à jour à la date de cette publication (le graphique "plate-forme inconnue" est Internet Explorer 11.15.16299.0):

Safari 11.1.2 Firefox 61.0 Chrome 68.0.3440.75 Internet Explorer 11.15.16299.0

D'après ces résultats, il semble que la méthode de préallocation 1 soit le pari multi-navigateur le plus sûr.


1

En supposant nodeList = document.querySelectorAll("div")qu'il s'agit d'une forme concise de conversion nodelisten tableau.

var nodeArray = [].slice.call(nodeList);

Voyez-moi l'utiliser ici .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.