Para cada matriz en javascript?

¿Cómo puedo ver todas las entradas en una matriz usando javascript?

Pensé que era algo como esto:

 forEach(instance in theArray) 

Donde el theArray es mi matriz, pero parece mal.

4041
17 февр. Dante1986 ambientado el 17 de febrero 2012-02-17 16:51 '12 a las 4:51 p.m. 2012-02-17 16:51
@ 32 respuestas
  • 1
  • 2

Tl; DR

  • No lo use for-in si no lo usa con protección o, al menos, no sabe por qué lo puede morder.
  • Tus mejores apuestas suelen ser

    • bucle for-of (solo para ES2015 +),
    • Array#forEach ( spec | MDN ) (o some sus parientes) (solo ES5 +)
    • simple pasada for moda for bucles,
    • o for-in entrada con protección.

Pero todavía hay mucho por explorar, sigue leyendo ...


JavaScript tiene una poderosa semántica para transformar de forma cíclica matrices y objetos de tipo matriz. Dividí la respuesta en dos partes: opciones para matrices auténticas y opciones para cosas que son similares solo a las matrices, como el objeto de arguments , otros objetos iterables (ES2015 +), colecciones DOM, etc.

Rápidamente me doy cuenta de que ahora puedes usar las opciones de ES2015, incluso en motores ES5, enviando ES2015 a ES5. Encuentre "ES2015 transpiling" / "ES6 transpiling" para más ...

Bueno, mira nuestras opciones:

Para matrices reales

Tiene tres opciones en ECMAScript 5 ("ES5"), la versión más compatible en este momento, y dos más agregadas en ECMAScript 2015 ("ES2015", "ES6"):

  1. Utilice forEach y relacionado (ES5 +)
  2. Uso simple for bucle
  3. Utilizar adecuadamente for-in
  4. Utilice for-of (use iterador implícito) (ES2015 +)
  5. Usar iterador explícitamente (ES2015 +)

Detalles:

1. Utilice forEach y relacionado

En cualquier entorno moderno e indefinido (bueno, no en IE8), donde tiene acceso a las funciones de Array agregadas por ES5 (directamente o usando rellenos múltiples), puede usar forEach ( spec | MDN ):

 var a = ["a", "b", "c"]; a.forEach(function(entry) { console.log(entry); }); 

forEach acepta una función de devolución de llamada y, opcionalmente, un valor utilizado como this al llamar a esta devolución de llamada (no se utiliza arriba). Se llama a la devolución de llamada para que cada entrada en la matriz omita las entradas no existentes en matrices dispersas. Aunque usé solo un argumento arriba, la devolución de llamada se llama con tres: el valor de cada registro, el índice de este registro y el enlace a la matriz que está repitiendo (en caso de que su función no lo tenga ya).

Si no es compatible con los navegadores antiguos, como IE8 (que NetApps muestra en más del 4% del mercado en el momento de escribir este artículo en septiembre de 2016), puede usar forEach en una página web universal sin un diseño de página. Si necesita admitir navegadores desactualizados, es fácil realizar la operación de forEach / forEach operación (busque "es5 shim" para varias opciones).

forEach tiene la ventaja de que no necesita declarar la indexación y los valores variables en el área de contenido, ya que se suministran como argumentos a la función de iteración y están muy bien orientados a esta iteración.

Si le preocupa el tiempo que lleva ejecutar una llamada de función para cada registro de matriz, no lo haga; detalles

Además, forEach es una función de "recorrer todos ellos", pero ES5 ha definido varias otras "elaboraciones útiles a través de funciones de matriz y cosas", que incluyen:

  • every (detiene el bucle la primera vez que la devolución de llamada devuelve false o algo falso)
  • some (detiene el bucle la primera vez que la devolución de llamada devuelve true o algo plausible)
  • filter (crea una nueva matriz que incluye elementos en los que la función de filtro devuelve true y omite aquellos donde devuelve false )
  • map (crea una nueva matriz de valores de devolución de llamada)
  • reduce (incrementa un valor, vuelve a invocar una devolución de llamada, pasa valores anteriores, consulta la especificación para obtener detalles, es útil para sumar el contenido de una matriz y muchas otras cosas)
  • reduceRight (por ejemplo, reduce , pero funciona en orden descendente, no en orden ascendente)

2. Usa un simple for bucle.

A veces las formas antiguas son las mejores:

 var index; var a = ["a", "b", "c"]; for (index = 0; index < a.length; ++index) { console.log(a[index]); } 

Si la longitud de la matriz no cambia durante el ciclo, y su rendimiento es un código sensible (poco probable), una versión un poco más complicada de la captura con la longitud frontal puede ser un poco más rápida:

 var index, len; var a = ["a", "b", "c"]; for (index = 0, len = a.length; index < len; ++index) { console.log(a[index]); } 

Y / o contando atrás:

 var index; var a = ["a", "b", "c"]; for (index = a.length - 1; index >= 0; --index) { console.log(a[index]); } 

Pero con los mecanismos modernos de JavaScript, rara vez tienes que desenterrar este último jugo.

En ES2015 y superior, puede hacer que sus variables de índice y valor sean locales for bucles for :

 let a = ["a", "b", "c"]; for (let index = 0; index < a.length; ++index) { let value = a[index]; } //console.log(index); // Would cause "ReferenceError: index is not defined" //console.log(value); // Would cause "ReferenceError: value is not defined" 

Y cuando hace esto, no solo el value sino también el index recrean para cada iteración del bucle, es decir, los cierres creados en la etiqueta del bucle contienen una referencia al index (y el value ) creado para esta iteración en particular:

 let divs = document.querySelectorAll("div"); for (let index = 0; index < divs.length; ++index) { divs[index].addEventListener('click', e => { alert("Index is: " + index); }); } 

Si tuviera cinco divs, obtendría "El índice es: 0", si hace clic en el primero y "El índice es: 4", si hace clic en el último. Esto no funciona si usas var lugar de let .

3. Utilice for-in correctamente.

Obtendrá personas que le dicen que use for-in , pero esto no es for-in lo que es for-in . for-in escribe a través de las propiedades enumeradas del objeto, y no de los índices de matriz. Pedido no garantizado , incluso en ES2015 (ES6). ES2015 determina el orden de las propiedades de un objeto (usando [[OwnPropertyKeys]] , [[Enumerate]] , y las cosas que las usan como Object.getOwnPropertyKeys ), pero no especifica que la for-in seguirá ese orden. (Detalles en esta otra respuesta .)

Sin embargo, esto puede ser útil, especialmente para matrices dispersas , si usa las precauciones adecuadas:

 // 'a' is a sparse array var key; var a = []; a[0] = "a"; a[10] = "b"; a[10000] = "c"; for (key in a) { if (a.hasOwnProperty(key)  // These are explained /^0$|^[1-9]\d*$/.test(key)  // and then hidden key <= 4294967294 // away below ) { console.log(a[key]); } } 

Tenga en cuenta los dos controles:

  1. Que el objeto tiene su propiedad bajo este nombre (y no el que hereda de su prototipo), y

  2. Que la clave es una cadena numérica de base 10 en su forma habitual de cadena, y su valor es <= 2 ^ 32 - 2 (que es 4,294,967,294). ¿De dónde viene este número? Esto es parte de la definición del índice de matriz en la especificación . Otros números (números no enteros, números negativos, números mayores de 2 ^ 32 - 2) no son índices de matriz. La causa de 2 ^ 32 - 2 es lo que hace que el valor de índice más alto sea menor que 2 ^ 32 - 1 , que es el valor máximo de la length matriz. (Por ejemplo, la longitud de la matriz corresponde a un entero sin signo de 32 bits). (Una confirmación de RobG para indicar en un comentario de mi publicación de blog que mi prueba anterior no era la correcta).

Esta es una pequeña cantidad de sobrecarga adicional para cada iteración de bucles en la mayoría de los arreglos, pero si tiene una matriz dispersa, esta puede ser una forma más eficiente de realizar un bucle, ya que estos son solo bucles para los registros que realmente existen. Por ejemplo, para la matriz anterior, hacemos un bucle tres veces (para las teclas "0" , "10" y "10000" ; recuerde que estas son líneas) y no 10,001 veces.

Ahora no quiere escribirlo cada vez, así que puede ponerlo en su caja de herramientas:

 function arrayHasOwnIndex(array, prop) { return array.hasOwnProperty(prop)  /^0$|^[1-9]\d*$/.test(prop)  prop <= 4294967294; // 2^32 - 2 } 

Y luego lo usaremos así:

 for (key in a) { if (arrayHasOwnIndex(a, key)) { console.log(a[key]); } } 

O, si solo está interesado en la prueba "lo suficientemente bueno para la mayoría de los casos", puede usarla, pero mientras esté cerca, esto no es del todo correcto:

 for (key in a) { // "Good enough" for most cases if (String(parseInt(key, 10)) === key  a.hasOwnProperty(key)) { console.log(a[key]); } } 

4. Utilice for-of (use iterador implícito) (ES2015 +)

ES2015 añade iteradores a JavaScript. La forma más fácil de usar los iteradores es el nuevo operador for-of . Se parece a esto:

 var val; var a = ["a", "b", "c"]; for (val of a) { console.log(val); } 

Conclusión:

 un b c

Debajo de las cubiertas, que obtiene un iterador de la matriz y pasa a través de él, obtiene valores de él. No tiene problemas con el uso de for-in , porque usa un iterador definido por un objeto (matriz), y las matrices determinan que sus iteradores repiten sus registros (y no sus propiedades). A diferencia de for-in en ES5, el orden en que se visualizan los registros es el orden numérico de sus índices.

5. Utilice el iterador explícitamente (ES2015 +)

A veces puedes usar explícitamente un iterador. También puedes hacer esto, aunque es mucho más complicado que for-of . Se parece a esto:

 var a = ["a", "b", "c"]; var it = a.values(); var entry; while (!(entry = it.next()).done) { console.log(entry.value); } 

Un iterador es un objeto que coincide con la definición de Iterator en la especificación. Su next método devuelve un nuevo objeto de resultado cada vez que lo llamas. El objeto resultante tiene una propiedad, done , nos dice si se hizo, y el value propiedad con el valor para esta iteración. ( done opcional si sería false , el value es opcional si no es undefined ).

El value varía dependiendo del iterador; las matrices admiten (al menos) tres funciones que devuelven iteradores:

  • values() : este es el que he usado anteriormente. Devuelve un iterador, donde cada value es un registro de matriz para esta iteración ( "a" , "b" y "c" en el ejemplo anterior).
  • keys() : devuelve un iterador, donde cada value es la clave para esta iteración (por lo tanto, para el nuestro arriba, habrá a "0" , y luego "1" , y luego "2" ).
  • entries() : devuelve un iterador, donde cada value es una matriz en forma de [key, value] para esta iteración.

Para objetos como una matriz.

Además de las matrices verdaderas, también hay objetos de tipo matriz que tienen la propiedad de length y las propiedades con nombres numéricos: instancias de NodeList , un objeto de arguments , etc. ¿Cómo hacemos una NodeList sus contenidos?

Utilice cualquiera de los parámetros anteriores para matrices.

Al menos algunas, y quizás la mayoría o incluso todas las matrices anteriores se usan a menudo igualmente bien para los objetos del tipo de matriz:

  1. Utilice forEach y relacionado (ES5 +)

    Las diversas funciones en Array.prototype son "genéricamente intencionalmente" y, por lo general, se pueden usar para objetos como una matriz a través de Function#call Function#apply o Function#apply . (Consulte la Exención de responsabilidad para los objetos proporcionados por el host al final de esta respuesta, pero este es un problema poco frecuente).

    Supongamos que desea utilizar forEach in childNodes Node childNodes . Harías esto:

     Array.prototype.forEach.call(node.childNodes, function(child) { // Do something with 'child' }); 

    Si va a hacer esto mucho, es posible que desee obtener una copia de la referencia de la función en una variable para reutilizarla, por ejemplo:

     // (This is all presumably in some scoping function) var forEach = Array.prototype.forEach; // Then later... forEach.call(node.childNodes, function(child) { // Do something with 'child' }); 
  2. Uso simple for bucle

    Obviamente, un simple bucle for se aplica a objetos como una matriz.

  3. Utilizar adecuadamente for-in

    for-in con las mismas garantías que con una matriz, deberían funcionar con objetos similares a una matriz; Es posible que se requieran reservaciones para los artículos proporcionados por el anfitrión al número 1 anterior.

  4. Utilice for-of (use iterador implícito) (ES2015 +)

    for-of usará el iterador proporcionado por el objeto (si lo hay); tendremos que ver cómo sucede esto con varios objetos similares a matrices, especialmente con archivos host. Por ejemplo, la especificación para el NodeList de querySelectorAll ha actualizado para admitir la iteración. La especificación para la HTMLCollection de getElementsByTagName no lo getElementsByTagName .

  5. Usar iterador explícitamente (ES2015 +)

    Vea el número 4, necesitamos ver cómo se desarrollan los iteradores.

Crea una verdadera matriz

En otros casos, puede convertir un objeto como una matriz en una matriz verdadera. Hacer esto es sorprendentemente fácil:

  1. Utilice el método de matriz de división

    Podemos usar el método de matriz de división, que, como los otros métodos mencionados anteriormente, es "genéricamente intencionalmente" y, por lo tanto, puede utilizarse con objetos similares a una matriz, por ejemplo:

     var trueArray = Array.prototype.slice.call(arrayLikeObject); 

    Entonces, por ejemplo, si queremos convertir un NodeList a una matriz real, podríamos hacer esto:

     var divs = Array.prototype.slice.call(document.querySelectorAll("div")); 

    Consulte "Precaución para los objetos proporcionados por el host" a continuación. En particular, tenga en cuenta que esto no funcionará en IE8 y versiones anteriores, lo que no permite el uso de objetos proporcionados por el host como this .

  2. Utilizar sintaxis de distribución ( ... )

    También es posible utilizar la sintaxis de distribución de ES2015 con mecanismos de JavaScript que admiten esta función:

     var trueArray = [...iterableObject]; 

    Entonces, por ejemplo, si queremos convertir un NodeList a una matriz verdadera, con la sintaxis de propagación, se vuelve bastante breve:

     var divs = [...document.querySelectorAll("div")]; 
  3. Utilice Array.from (spec) | (MDN)

    Array.from (ES2015 +, pero fácilmente rellenado con polietileno) crea una matriz a partir de un objeto como una matriz, si es necesario, primero pasa los registros a través de la función correspondiente. Entonces

     var divs = Array.from(document.querySelectorAll("div")); 

    O, si desea obtener una matriz de nombres de etiquetas para elementos con una clase determinada, debe usar la función de mapeo:

     // Arrow function (ES2015): var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName); // Standard function (since 'Array.from' can be shimmed): var divs = Array.from(document.querySelectorAll(".some-class"), function(element) { return element.tagName; }); 

Precaución para las instalaciones proporcionadas por el anfitrión

Si usa las funciones Array.prototype con objetos como hosts (listas DOM y otras cosas proporcionadas por el navegador, en lugar del motor de JavaScript), debe asegurarse de probar en sus entornos de destino para asegurarse de que el objeto host proporcionado se comporte es correcto La mayoría de ellos se comportan correctamente (ahora), pero es importante verificar. La razón es que la mayoría de los métodos de Array.prototype que probablemente quiera usar se basan en el objeto proporcionado por el host, dando una respuesta honesta al resumen [[HasProperty]] . En el momento de escribir este artículo, los navegadores hicieron un muy buen trabajo, pero la especificación 5.1 sugirió que el objeto proporcionado por el host podría no ser justo. Esto está en §8.6.2 , unos pocos párrafos debajo de la tabla grande al comienzo de esta sección, donde dice:

Los objetos host pueden implementar estos métodos internos de cualquier manera a menos que se indique lo contrario; por ejemplo, una posibilidad es que [[Get]] y [[Put]] para un objeto host en particular realmente recuperen y guarden valores de propiedades, pero [[HasProperty]] siempre generan falsos .

(No pude encontrar una redacción equivalente en la especificación ES2015, pero todavía tiene que serlo). De nuevo, al escribir un host común de la matriz de tipo de objeto proporcionada en los navegadores modernos [instancias de NodeList , por ejemplo], haga que el [[HasProperty]] correcto, pero importante comprobar.)

6292
17 февр. La respuesta es dada por TJ Crowder el 17 de febrero. 2012-02-17 16:53 '12 a las 4:53 pm 2012-02-17 16:53

Edición : esta respuesta está totalmente desactualizada. Para un enfoque más moderno, mira los métodos disponibles en la matriz . Los métodos de interés pueden ser:

  • para cada
  • tarjeta
  • el filtro
  • cierre de cremallera
  • para reducir
  • cada
  • un poco

La forma estándar de iterar una matriz en JavaScript es for -loop vanilla:

border=0
 var length = arr.length, element = null; for (var i = 0; i < length; i++) { element = arr[i]; // Do something with element } 

Sin embargo, tenga en cuenta que este enfoque es bueno solo si tiene una matriz densa y cada índice está ocupado por un elemento. Si la matriz es escasa, con este enfoque puede tener problemas de rendimiento, ya que pasará por muchos índices que en realidad no existen en la matriz. En este caso, es mejor usar for.. in -loop. Sin embargo, debe tomar las precauciones adecuadas para asegurarse de que solo estén for..in las propiedades requeridas de la matriz (es decir, los elementos de la matriz), porque for..in ... for..in -loop también se incluirá en los navegadores heredados, o si las propiedades adicionales se definen como enumerable .

En ECMAScript 5 , el método forEach se usará para el prototipo de matriz, pero no es compatible con los navegadores heredados. Por lo tanto, para poder usarlo de manera consistente, debe tener un entorno que lo admita (por ejemplo, Node.js para JavaScript del lado del servidor), o usar "Polyfill". El Polyfill para esta funcionalidad, sin embargo, es trivial, y como hace que el código sea más fácil de leer, debe incluirse en el Polyfill.

470
17 февр. Respuesta dada por PatrikAkerstrand el 17 de febrero. 2012-02-17 16:55 '12 a las 4:55 pm 2012-02-17 16:55

Si está utilizando la biblioteca jQuery , puede usar jQuery.each :

 var length = yourArray.length; for (var i = 0; i < length; i++) { // Do something with yourArray[i]. } 
214
17 февр. La respuesta se da Poonam 17 de febrero. 2012-02-17 17:01 '12 a las 17:01 2012-02-17 17:01

Bucle de vuelta

Creo que lo contrario para el ciclo merece una mención aquí:

 for (var i = array.length; i--; ) { // process array[i] } 

Beneficios:

  • No es necesario declarar una variable temporal len o compararla con array.length en cada iteración, que puede ser una optimización minuciosa.
  • Eliminar a los hermanos del DOM en orden inverso suele ser más eficiente . (El navegador necesita mover menos elementos en las matrices internas).
  • Si cambia una matriz durante un bucle, en o después de un índice I (por ejemplo, borra o inserta un elemento en la array[i] ), un bucle directo salta un elemento que se desplaza hacia la izquierda hasta la posición i o vuelve a verificar el elemento i-th que se desplazó a la derecha En el bucle tradicional, puede actualizar i para que apunte al siguiente elemento que debe procesarse: 1, pero simplemente cambiar la dirección de la iteración suele ser una solución más simple y elegante .
  • De manera similar, al cambiar o eliminar elementos DOM anidados , el procesamiento inverso puede omitir errores . Por ejemplo, considere cambiar el InternalHTML del nodo padre antes de procesar sus hijos. En el momento en que se llegue al nodo secundario, se separará del DOM y se reemplazará con un nuevo descendiente cuando se escribió el innerHTML principal.
  • Más corto y leído que algunas otras opciones disponibles. Aunque pierde a forEach() y ES6 for ... of .

Desventajas:

  • Procesa los artículos en orden inverso. Si creó una nueva matriz a partir de los resultados o imprimió en la pantalla, la salida, por supuesto, se cancelará en relación con el pedido original.
  • Неоднократно вставляя братьев и сестер в DOM в качестве первого ребенка, чтобы сохранить их порядок, менее эффективен . (В браузере все равно придется перекладывать вещи.) Чтобы создавать узлы DOM эффективно и упорядоченно, просто переходите вперед и добавьте как обычно (а также используйте "фрагмент документа" ).
  • Обратный цикл запутывает для младших разработчиков. (Вы можете считать это преимущество, в зависимости от вашего прогноза.)

Должен ли я всегда использовать его?