¿La forma más rápida de convertir JavaScript NodeList a Array?


251

Las preguntas respondidas anteriormente aquí dijeron que esta era la forma más rápida:

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

Al realizar una evaluación comparativa en mi navegador, descubrí que es más de 3 veces más lento que esto:

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

Ambos producen el mismo resultado, pero me resulta difícil creer que mi segunda versión sea la forma más rápida posible, especialmente porque la gente ha dicho lo contrario aquí.

¿Es esto una peculiaridad en mi navegador (Chromium 6)? ¿O hay una manera más rápida?

EDITAR: Para cualquier persona que se preocupe, me decidí por lo siguiente (que parece ser el más rápido en todos los navegadores que probé):

//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: encontré una forma aún más rápida

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

2
arr[arr.length] = nl[i];puede ser más rápido que arr.push(nl[i]);ya que evita una llamada de función.
Luc125

99
Esta página jsPerf realiza un seguimiento de todas las respuestas en esta página: jsperf.com/nodelist-to-array/27
pilau

Tenga en cuenta que "EDIT2: Encontré una forma más rápida" es un 92% más lento en IE8.
Camilo Martin

2
Como ya sabes cuántos nodos tienes:var i = nl.length, arr = new Array(i); for(; i--; arr[i] = nl[i]);
mems

@ Luc125 Depende del navegador, ya que la implementación de inserción puede estar optimizada, estoy pensando en Chrome porque v8 es bueno con este tipo de cosas.
axelduch

Respuestas:


197

El segundo tiende a ser más rápido en algunos navegadores, pero el punto principal es que debe usarlo porque el primero simplemente no es cruzado. A pesar de que The Times They are a-Changin '

@kangax ( vista previa de IE 9 )

Array.prototype.slice ahora puede convertir ciertos objetos host (por ejemplo, NodeList) en matrices, algo que la mayoría de los navegadores modernos han podido hacer durante bastante tiempo.

Ejemplo:

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

??? Ambos son compatibles entre navegadores: Javascript (al menos si afirma ser compatible con la especificación ECMAscript) es Javascript; La matriz, el prototipo, el segmento y la llamada son características del lenguaje principal + tipos de objeto.
Jason S

66
pero no se pueden usar en NodeLists en IE (sé que apesta, pero oye ver mi actualización)
gblazex

99
Debido a que las NodeLists no son parte del lenguaje, son parte de la API DOM, que se sabe que es defectuosa / impredecible, especialmente en IE
gblazex

3
Array.prototype.slice no es un navegador cruzado, si tiene en cuenta IE8.
Lajos Meszaros

1
Sí, de eso se trataba básicamente mi respuesta :) Aunque fue más relevante en 2010 que en hoy (2015).
gblazex

224

Con ES6, ahora tenemos una forma sencilla de crear una matriz a partir de una NodeList: la Array.from()función.

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

¿Cómo se compara en velocidad este nuevo método ES6 con los otros mencionados anteriormente?
user5508297

10
@ user5508297 Mejor que el truco de llamada de corte. Más lento que los bucles for, pero eso no es exactamente lo que queremos tener una matriz sin atravesarla. ¡Y la sintaxis es hermosa, simple y fácil de recordar!
webdif

Lo bueno de Array.from es que puede usar el argumento del mapa:console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]
honzajde


19

Algunas optimizaciones:

  • guardar la longitud de la NodeList en una variable
  • establezca explícitamente la longitud de la nueva matriz antes de configurar.
  • acceder a los índices, en lugar de empujar o no cambiar.

Código ( jsPerf ):

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

Puede reducir un poco más de tiempo utilizando Array (longitud) en lugar de crear la matriz y luego dimensionarla por separado. Si luego usa const para declarar la matriz y su longitud, con un let dentro del ciclo, termina aproximadamente 1.5% más rápido que el método anterior: const a = Array (nl.length), c = a.length; para (let b = 0; b <c; b ++) {a [b] = nl [b]; } ver jsperf.com/nodelist-to-array/93
BrianFreud el

15

Los resultados dependerán completamente del navegador, para dar un veredicto objetivo, tenemos que hacer algunas pruebas de rendimiento, aquí hay algunos resultados, puede ejecutarlos aquí :

Chrome 6:

Firefox 3.6:

Firefox 4.0b2:

Safari 5:

Vista previa de la plataforma IE9 3:


1
Me pregunto cómo se mantiene el bucle inverso para estos ... for (var i=o.length; i--;)... ¿el 'bucle for' en estas pruebas reevaluó la propiedad de longitud en cada iteración?
Dagg Nabbit


9

En ES6 puedes usar:

  • Array.from

    let array = Array.from(nodelist)

  • Operador extendido

    let array = [...nodelist]


No agrega nada nuevo de lo que ya se ha escrito en respuestas anteriores.
Stefan Becker

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

Ahora puede hacer document.querySelectorAll ('div'). ForEach (function () ...)


Buena idea, gracias @John! Sin embargo, NodeListno funciona pero sí Object: Object.prototype.forEach = Array.prototype.forEach; document.getElementsByTagName("img").forEach(function(img) { alert(img.src); });
Ian Campbell

3
No use Object.prototype: rompe JQuery y un montón de cosas como la sintaxis literal del diccionario.
Nate Symer

Claro, evite extender las funciones integradas nativas.
roland

5

más rápido y más corto:

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

3
¿Por qué el >>>0? ¿Y por qué no poner las tareas en la primera parte del ciclo for?
Camilo Martin

55
Además, esto tiene errores. Cuando les así 0, el ciclo finalizará, por lo tanto, el 0elemento th no se copiará (recuerde que hay un elemento en el índice 0)
Camilo Martin

1
Me encanta esta respuesta, pero ... Cualquiera que se pregunte: >>> puede que no sea necesario aquí, pero se utiliza para garantizar que la longitud de la lista de nodos se adhiera a las especificaciones de la matriz; asegura que es un entero de 32 bits sin signo. Compruébelo aquí ecma-international.org/ecma-262/5.1/#sec-15.4 Si le gusta el código ilegible, ¡use este método con las sugerencias de @ CamiloMartin!
Todd

En respuesta a @CamiloMartin: es arriesgado poner vardentro de la primera parte de un forbucle debido a la 'elevación variable'. La declaración de var'se izará' en la parte superior de la función, incluso si la varlínea aparece en algún lugar más abajo, y esto puede causar efectos secundarios que no son obvios en la secuencia del código. Por ejemplo algo de código en la misma función que ocurre antes el bucle podría depender en ay lsiendo no declarado. Por lo tanto, para una mayor capacidad de predicción, declare sus variables en la parte superior de la función (o si está en ES6, use consto en su letlugar, que no se iza).
brennanyoung

3

Mira esta publicación de blog aquí que habla sobre lo mismo. Por lo que deduzco, el tiempo extra podría tener que ver con subir la cadena del telescopio.


Interesante. Acabo de hacer algunas pruebas similares ahora y Firefox 3.6.3 no muestra un aumento en la velocidad de ninguna manera, mientras que Opera 10.6 tiene un aumento del 20% y Chrome 6 tiene un aumento del 230% (!) Haciéndolo de forma manual.
jairajs89

@ jairajs89 bastante extraño. Parece que Array.prototype.slicedepende del navegador. Me pregunto qué algoritmo está usando cada uno de los navegadores.
Vivin Paliath

3

Esta es la función que uso en mi JS:

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

1

Aquí hay gráficos actualizados a la fecha de esta publicación (el cuadro de "plataforma desconocida" es Internet Explorer 11.15.16299.0):

Safari 11.1.2 Firefox 61.0 Chrome 68.0.3440.75 Internet Explorer 11.15.16299.0

A partir de estos resultados, parece que el método de preasignación 1 es la apuesta más segura entre navegadores.


1

Suponiendo nodeList = document.querySelectorAll("div"), esta es una forma concisa de convertir nodelista matriz.

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

Véame usarlo aquí .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.