For-each sobre una matriz en JavaScript

5112

¿Cómo puedo recorrer todas las entradas en una matriz usando JavaScript?

Pensé que era algo como esto:

forEach(instance in theArray)

¿Dónde theArrayestá mi matriz, pero esto parece ser incorrecto?

0
7583
+50

TL; DR

  • Tus mejores apuestas suelen ser

    • un for-ofbucle (solo ES2015 +; especificaciones | MDN ) - simple y asyncamigable
    • forEach(ES5 + solamente; especificación | MDN ) (o sus parientes somey tal) - no async amigable (pero ver detalles)
    • un simple forbucle anticuado - async-amistoso
    • (raramente) for-in con salvaguardas - async-amistoso
  • Algunos "no" rápidos:

    • No lo use afor-in menos que lo use con medidas de seguridad o al menos sepa por qué podría morderlo.
    • No lo use mapsi no está usando su valor de retorno .
      (Lamentablemente, hay alguien enseñando map[ spec / MDN ] como si lo fuera forEach , pero no es para eso . Si no está usando la matriz que crea, no la use map).
    • No lo useforEach si la devolución de llamada hace un trabajo asincrónico y desea forEachque espere hasta que ese trabajo esté terminado (porque no lo hará).

Pero hay mucho más para explorar, sigue leyendo ...


JavaScript tiene una semántica poderosa para recorrer matrices y objetos similares a matrices. He dividido la respuesta en dos partes: opciones para matrices genuinas y opciones para cosas que son simplemente como matrices, como el argumentsobjeto, otros objetos iterables (ES2015 +), colecciones DOM, etc.

Bien, veamos nuestras opciones:

Para matrices reales

Tiene cinco opciones (dos admitidas básicamente para siempre, otra agregada por ECMAScript 5 ["ES5"] y dos más agregadas en ECMAScript 2015 ("ES2015", también conocido como "ES6"):

  1. Usar for-of(usar un iterador implícitamente) (ES2015 +)
  2. Uso forEachy relacionados (ES5 +)
  3. Usa un forbucle simple
  4. Utilizar for-in correctamente
  5. Utilice un iterador de forma explícita (ES2015 +)

(Puede ver esas especificaciones antiguas aquí: ES5 , ES2015 , pero ambas han sido reemplazadas; el borrador del editor actual siempre está aquí ).

Detalles:

1. Use for-of(use un iterador implícitamente) (ES2015 +)

ES2015 agregó iteradores e iterables a JavaScript. Las matrices son iterables (también lo son las cadenas, Maps y Sets, así como las colecciones y listas DOM, como verá más adelante). Los objetos iterables proporcionan iteradores para sus valores. La nueva for-ofdeclaración recorre los valores devueltos por un iterador:

const a = ["a", "b", "c"];
for (const val of a) { // You can use `let` instead of `const` if you like
    console.log(val);
}
// a
// b
// c

¡No hay nada más sencillo que eso! Debajo de las cubiertas, eso obtiene un iterador de la matriz y recorre los valores que devuelve el iterador. El iterador proporcionado por las matrices proporciona los valores de los elementos de la matriz, en orden de principio a fin.

Observe cómo valse define el alcance de cada iteración de bucle; intentar usar valdespués del final del ciclo fallaría porque no existe fuera del cuerpo del ciclo.

En teoría, un for-ofbucle implica varias llamadas a funciones (una para obtener el iterador, luego otra para obtener cada valor de él). Incluso cuando eso es cierto, no hay nada de qué preocuparse, las llamadas a funciones son muy baratas en los motores JavaScript modernos (me molestó forEach[a continuación] hasta que lo miré; detalles ). Pero, además, los motores de JavaScript optimizan esas llamadas (en código crítico para el rendimiento) cuando se trata de iteradores nativos para cosas como matrices.

for-ofes totalmente asyncamigable. Si necesita que el trabajo en un cuerpo de bucle se realice en serie (no en paralelo), un awaitcuerpo de bucle esperará a que se establezca la promesa antes de continuar. Aquí hay un ejemplo tonto:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    for (const str of messages) {
        await delay(400);
        console.log(str);
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

Observe cómo aparecen las palabras con un retraso antes de cada una.

Es una cuestión de estilo de codificación, pero for-ofes lo primero que busco cuando recorro algo iterable.

2. Uso forEachy relacionados

En cualquier entorno incluso vagamente moderno (por lo tanto, no IE8) donde tenga acceso a las Arrayfunciones agregadas por ES5, puede usar forEach( spec | MDN ) si solo está tratando con código síncrono (o no necesita esperar para que un proceso asincrónico termine durante el ciclo):

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

forEachacepta una función de devolución de llamada y, opcionalmente, un valor para usar como thiscuando se llama a esa devolución de llamada (no se usa arriba). Se llama a la devolución de llamada para cada entrada en la matriz, en orden, omitiendo entradas inexistentes en matrices dispersas. Aunque solo usé un parámetro arriba, la devolución de llamada se llama con tres argumentos: el valor de cada entrada, el índice de esa entrada y una referencia a la matriz sobre la que está iterando (en caso de que su función aún no la tenga práctico).

Me gusta for-of, forEachtiene la ventaja de que no es necesario declarar variables de valor e indexación en el ámbito contenedor; en este caso, se suministran como argumentos para la función de iteración y, por lo tanto, se ajustan muy bien a esa iteración.

A diferencia for-of, forEachtiene la desventaja de que no comprende asyncfunciones y await. Si utiliza una asyncfunción que la de devolución de llamada, forEachno sin esperar a que la promesa de que la función se asiente antes de continuar. Aquí está el asyncejemplo de for-ofusar en su forEachlugar: observe cómo hay un retraso inicial, pero luego todo el texto aparece de inmediato en lugar de esperar:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    messages.forEach(async message => { // Doesn't wait before continuing
        await delay(400);
        console.log(message);
    });
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

forEach es la función "recorrerlos todos", pero ES5 definió varias otras funciones útiles "trabajar a través de la matriz y hacer cosas", que incluyen:

  • every( spec | MDN ) - deja de reproducirse la primera vez que la devolución de llamada devuelve un valor falso
  • some( spec | MDN ): deja de repetirse la primera vez que la devolución de llamada devuelve un valor verdadero
  • filter( spec | MDN ): crea una nueva matriz que incluye elementos donde la devolución de llamada devuelve un valor verdadero, omitiendo aquellos en los que no lo hace
  • map( spec | MDN ): crea una nueva matriz a partir de los valores devueltos por la devolución de llamada
  • reduce( spec | MDN ): crea un valor llamando repetidamente a la devolución de llamada, pasando los valores anteriores; ver las especificaciones para los detalles
  • reduceRight( spec | MDN ) - como reduce, pero funciona en orden descendente en lugar de ascendente

Al igual que con forEach, si usa una asyncfunción como su devolución de llamada, ninguno de ellos espera a que se establezca la promesa de la función. Eso significa:

  • Usar una asyncdevolución de llamada de función nunca es apropiado con every, somey filterdado que tratarán la promesa devuelta como si fuera un valor verdadero; que no esperan a que la promesa se asiente y luego utilizar el valor cumplimiento.
  • El uso de una asyncllamada de función es a menudo apropiado con map, si el objetivo es convertir una matriz de algo en una serie de promesas , tal vez para pasar a una de las funciones promesa de combinadores ( Promise.all, Promise.race, promise.allSettled, o Promise.any).
  • El uso de una asyncdevolución de llamada de función rara vez es apropiado con reduceo reduceRight, porque (nuevamente) la devolución de llamada siempre devolverá una promesa. Pero hay un modismo de construir una cadena de promesas a partir de una matriz que usa reduce( const promise = array.reduce((p, element) => p.then(/*...something using `element`...*/));), pero generalmente en esos casos un ciclo for-ofo foren una asyncfunción será más claro y más fácil de depurar.

3. Usa un forbucle simple

A veces, las formas antiguas son las mejores:

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

Si la longitud de la matriz no cambiará durante el bucle, y es en el código sensibles al rendimiento (poco probable), una versión ligeramente más complicado agarrar la longitud en la delantera podría ser un pequeño poco más rápido:

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

Y / o contando hacia atrás:

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

Pero con los motores JavaScript modernos, es raro que necesite sacar ese último jugo.

Antes de ES2015, la variable de bucle tenía que existir en el alcance contenedor, porque varsolo tiene alcance a nivel de función, no alcance a nivel de bloque. Pero como vio en los ejemplos anteriores, puede usar letdentro del forpara establecer el alcance de las variables solo en el bucle. Y cuando haces eso, la indexvariable se vuelve a crear para cada iteración de bucle, lo que significa que los cierres creados en el cuerpo del bucle mantienen una referencia al indexpara esa iteración específica, lo que resuelve el antiguo problema de "cierres en bucles":

const divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        console.log("Index is: " + index);
    });
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>

En lo anterior, obtiene "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 usa en varlugar de let.

Como for-of, los forbucles funcionan bien en asyncfunciones. Aquí está el ejemplo anterior usando un forbucle:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    for (let i = 0; i < messages.length; ++i) {
        await delay(400);
        console.log(messages[i]);
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

4. Use for-in correctamente

for-inno es para recorrer matrices, es para recorrer los nombres de las propiedades de un objeto. A menudo parece funcionar para recorrer matrices como un subproducto del hecho de que las matrices son objetos, pero no solo recorre los índices de la matriz, sino que recorre todas las propiedades enumerables del objeto (incluidas las heredadas). (También solía ser que no se especificaba el orden; ahora es [detalles en esta otra respuesta ], pero aunque el orden se especifica ahora, las reglas son complejas, hay excepciones y confiar en el orden no es mejores prácticas.)

Los únicos casos de uso reales para for-inuna matriz son:

  • Es una matriz dispersa con enormes lagunas, o
  • Estás usando propiedades que no son elementos y quieres incluirlas en el ciclo

Mirando solo ese primer ejemplo: puede usar for-inpara visitar esos elementos de matriz dispersos si usa las salvaguardas adecuadas:

// `a` is a sparse array
const a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (const name in a) {
    if (a.hasOwnProperty(name)  &&      // These checks are
        /^0$|^[1-9]\d*$/.test(name) &&  // explained
        name <= 4294967294              // below
       ) {
        console.log(a[name]);
    }
}

Tenga en cuenta los tres controles:

  1. Que el objeto tiene su propia propiedad con ese nombre (no una que herede de su prototipo), y

  2. Que el nombre es todo dígitos decimales (por ejemplo, forma de cadena normal, no notación científica), y

  3. Que el valor del nombre cuando se coacciona a un número es <= 2 ^ 32 - 2 (que es 4,294,967,294). ¿De dónde viene ese número? Es parte de la definición de un índice de matriz en la especificación . Otros números (no enteros, números negativos, números mayores que 2 ^ 32 - 2) no son índices de matriz. La razón por la que es 2 ^ 32 - 2 es que hace que el valor de índice más grande sea inferior a 2 ^ 32 - 1 , que es el valor máximo que lengthpuede tener una matriz . (Por ejemplo, la longitud de una matriz se ajusta a un entero sin signo de 32 bits).

Por supuesto, no haría eso en código en línea. Escribirías una función de utilidad. Quizás:

// Utility function for antiquated environments without `forEach`
const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
const rexNum = /^0$|^[1-9]\d*$/;
function sparseEach(array, callback, thisArg) {
    for (const name in array) {
        const index = +name;
        if (hasOwn(a, name) &&
            rexNum.test(name) &&
            index <= 4294967294
           ) {
            callback.call(thisArg, array[name], index, array);
        }
    }
}

const a = [];
a[5] = "five";
a[10] = "ten";
a[100000] = "one hundred thousand";
a.b = "bee";

sparseEach(a, (value, index) => {
    console.log("Value at " + index + " is " + value);
});

... aunque dicho esto, la mayoría del código solo hace la hasOwnPropertyverificación.

Al igual que for, for-infunciona bien en funciones asincrónicas si el trabajo dentro de él debe realizarse en serie.

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    for (const name in messages) {
        if (messages.hasOwnProperty(name)) {
            await delay(400);
            console.log(messages[name]);
        }
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

5. Utilice un iterador de forma explícita (ES2015 +)

for-ofutiliza un iterador implícitamente, haciendo todo el trabajo de corte por usted. A veces, es posible que desee utilizar un iterador de forma explícita . Se parece a esto:

const a = ["a", "b", "c"];
const it = a.values(); // Or `const it = a[Symbol.iterator]();` if you like
let entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

Un iterador es un objeto que coincide con la definición de iterador en la especificación. Su nextmétodo devuelve un nuevo objeto de resultado cada vez que lo llama. El objeto de resultado tiene una propiedad, doneque nos dice si está hecho, y una propiedad valuecon el valor de esa iteración. ( donees opcional si lo fuera false, valuees opcional si lo fuera undefined).

Lo que obtienes valuevaría según el iterador. En arrays, el iterador predeterminado proporciona el valor de cada elemento de la matriz ( "a", "b"y "c"en el ejemplo anterior). Las matrices también tienen otros tres métodos que devuelven iteradores:

  • values(): Este es un alias para el [Symbol.iterator]método que devuelve el iterador predeterminado.
  • keys(): Devuelve un iterador que proporciona cada clave (índice) en la matriz. En el ejemplo anterior, proporcionaría "0", entonces "1", entonces "2"(sí, como cadenas).
  • entries(): Devuelve un iterador que proporciona [key, value]matrices.

Dado que los objetos iteradores no avanzan hasta que los llama next, funcionan bien en los asyncbucles de funciones. Aquí está el for-ofejemplo anterior usando el iterador explícitamente:

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function showSlowly(messages) {
    const it = messages.values()
    while (!(entry = it.next()).done) {
        await delay(400);
        console.log(entry.value);
    }
}

showSlowly([
    "So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects

Para objetos tipo matriz

Aparte de las matrices verdaderas, también hay objetos similares a matrices que tienen una lengthpropiedad y propiedades con nombres de todos los dígitos: NodeListinstancias , HTMLCollectioninstancias , el argumentsobjeto, etc. ¿Cómo recorremos su contenido?

Utilice la mayoría de las opciones anteriores

Al menos algunos, y posiblemente la mayoría o incluso todos, de los enfoques de matriz anteriores se aplican igualmente bien a objetos similares a matrices:

  1. Usar for-of(usar un iterador implícitamente) (ES2015 +)

    for-ofutiliza el iterador proporcionado por el objeto (si lo hay). Eso incluye objetos proporcionados por el host (como colecciones y listas DOM). Por ejemplo, las HTMLCollectioninstancias de getElementsByXYZmétodos y NodeListlas instancias s de querySelectorAllambos admiten la iteración. (Esto se define muy sutilmente por las especificaciones HTML y DOM Básicamente, cualquier objeto con. lengthY acceso indexado automáticamente iterable Es. No tiene que estar marcada iterable; que se utiliza sólo para colecciones que, además de ser iterable, el apoyo forEach, values, keysy entriesmétodos. lo NodeListhace; HTMLCollectionno lo hace, pero ambos son iterables).

    A continuación, se muestra un ejemplo de cómo recorrer divelementos en bucle :

const divs = document.querySelectorAll("div");
for (const div of divs) {
    div.textContent = Math.random();
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
  1. Uso forEachy relacionados (ES5 +)

    Las diversas funciones de Array.prototypeson "intencionadamente genéricas" y se pueden utilizar en objetos similares a matrices a través de Function#call( spec | MDN ) o Function#apply( spec | MDN ). (Si tiene que lidiar con IE8 o una versión anterior [ouch], consulte la "Advertencia para los objetos proporcionados por el host" al final de esta respuesta, pero no es un problema con los navegadores vagamente modernos).

    Suponga que quiere usar forEachen Nodela childNodescolección de a (que, al ser an HTMLCollection, no tiene de forEachforma nativa). Harías esto:

    Array.prototype.forEach.call(node.childNodes, (child) => {
        // Do something with `child`
    });
    

    (Tenga en cuenta, sin embargo, que es posible que utilices for-ofen node.childNodes.)

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

    // (This is all presumably in a module or some scoping function)
    const forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
    
    // Then later...
    forEach(node.childNodes, (child) => {
        // Do something with `child`
    });
    
  2. Usa un forbucle simple

    Quizás, obviamente, un forbucle simple funciona para objetos similares a matrices.

  3. Utilice un iterador de forma explícita (ES2015 +)

    Ver # 1.

Es posible que pueda salirse con la suya for-in(con salvaguardas), pero con todas estas opciones más apropiadas, no hay razón para intentarlo.

Crea una verdadera matriz

Otras veces, es posible que desee convertir un objeto similar a una matriz en una matriz verdadera. Hacer eso es sorprendentemente fácil:

  1. Usar Array.from

    Array.from (especificación) | (MDN) (ES2015 +, pero fácilmente con relleno múltiple) crea una matriz a partir de un objeto similar a una matriz, pasando opcionalmente las entradas a través de una función de mapeo primero. Entonces:

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

    ... toma el NodeListde querySelectorAlly hace una matriz a partir de él.

    La función de mapeo es útil si iba a mapear el contenido de alguna manera. Por ejemplo, si desea obtener una matriz de los nombres de las etiquetas de los elementos con una clase determinada:

    // Typical use (with an arrow function):
    const divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    
    // Traditional function (since `Array.from` can be polyfilled):
    var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
        return element.tagName;
    });
    
  2. Utilice la sintaxis de propagación ( ...)

    También es posible utilizar la sintaxis de propagación de ES2015 . Como for-of, esto usa el iterador provisto por el objeto (vea el # 1 en la sección anterior):

    const trueArray = [...iterableObject];
    

    Entonces, por ejemplo, si queremos convertir a NodeListen una verdadera matriz, con la sintaxis extendida, esto se vuelve bastante conciso:

    const divs = [...document.querySelectorAll("div")];
    
  3. Usa el slicemétodo de matrices

    Podemos usar el slicemétodo de matrices, que al igual que los otros métodos mencionados anteriormente es "intencionalmente genérico" y, por lo tanto, se puede usar con objetos similares a matrices, como este:

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

    Entonces, por ejemplo, si queremos convertir a NodeListen una verdadera matriz, podríamos hacer esto:

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

    (Si aún tiene que manejar IE8 [ouch], fallará; IE8 no le permitió usar objetos proporcionados por el host de thisesa manera).

Advertencia para los objetos proporcionados por el host

Si usa Array.prototypefunciones con objetos tipo matriz proporcionados por el host (por ejemplo, colecciones DOM y otras proporcionadas por el navegador en lugar del motor JavaScript), los navegadores obsoletos como IE8 no necesariamente se manejan de esa manera, por lo que si tiene que admitirlos , asegúrese de realizar la prueba en los entornos de destino. Pero no es un problema con los navegadores vagamente modernos. (Para entornos sin navegador, naturalmente dependerá del entorno).

2
  • ¿Qué quieres decir con propiedades que no son elementos? Aalexander 9 dic.20 a las 13:08
  • 2
    @Alex: propiedades de la matriz que no representan elementos de la matriz. Por ejemplo: const a = ["a", "b"]; a.example = 42;Esa matriz tiene tres propiedades (que no sean los todas las matrices tienen), cuyos nombres son las cuerdas "0", "1"y "example". La propiedad nombrada "example"es una propiedad que no es un elemento. Los otros dos son propiedades de elementos, porque representan los elementos de la matriz. T.J. Crowder 9/12/20 a las 13:18
547

Nota : Esta respuesta está irremediablemente desactualizada. Para un enfoque más moderno, observe los métodos disponibles en una matriz . Los métodos de interés pueden ser:

  • para cada
  • mapa
  • filtrar
  • Código Postal
  • reducir
  • cada
  • algunos

La forma estándar de iterar una matriz en JavaScript es un forbucle de vainilla :

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 solo es bueno si tiene una matriz densa y cada índice está ocupado por un elemento. Si la matriz es escasa, puede tener problemas de rendimiento con este enfoque, ya que iterará sobre muchos índices que realmente no existen en la matriz. En este caso, un for .. in-loop podría ser una mejor idea. Sin embargo , debe utilizar las medidas de seguridad adecuadas para asegurarse de que solo se actúe sobre las propiedades deseadas de la matriz (es decir, los elementos de la matriz), ya que el for..in-loop también se enumerará en los navegadores heredados, o si las propiedades adicionales se definen como enumerable.

En ECMAScript 5 habrá un método forEach en el prototipo de matriz, pero no es compatible con navegadores heredados. Entonces, 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 un "Polyfill". Sin embargo, el Polyfill para esta funcionalidad es trivial y, dado que hace que el código sea más fácil de leer, es un buen polyfill para incluir.

2
  • ¿Hay alguna forma de hacerlo en una sola línea de código? Por ejemplo en facebook me gusta acelerar videos con document.getElementsByTagName("video")[28].playbackRate = 2.2. Si pudiera mapear fácilmente todos los elementos, podría evitar tener que identificar qué video (por ejemplo, el índice 28 en este caso). ¿Algunas ideas? stevec 5 sep.20 a las 19:39
  • 1
    @stevec: Array.from (document.querySelectorAll ('video')). forEach (video => video.playbackRate = 2.2); PatrikAkerstrand 14 de septiembre de 2020 a las 6:24
259

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

$.each(yourArray, function(index, value) {
  // do your stuff here
});

EDITAR:

Según la pregunta, el usuario quiere código en javascript en lugar de jquery, por lo que la edición es

var length = yourArray.length;   
for (var i = 0; i < length; i++) {
  // Do something with yourArray[i].
}
0
134

Bucle al revés

Creo que el bucle for inverso merece una mención aquí:

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

Ventajas:

  • No es necesario declarar una lenvariable temporal o comparar array.lengthen cada iteración, cualquiera de las cuales podría ser una optimización mínima.
  • Eliminar a los hermanos del DOM en orden inverso suele ser más eficaz . (El navegador necesita realizar menos cambios de elementos en sus matrices internas).
  • Si modifica la matriz mientras realiza un bucle, en o después del índice i (por ejemplo, elimina o inserta un elemento en array[i]), entonces un bucle hacia adelante omitiría el elemento que se movió a la izquierda en la posición i , o volvería a procesar el elemento i que estaba desplazado a la derecha. En un bucle for tradicional, puede actualizar i para que apunte al siguiente elemento que necesita procesamiento: 1, pero simplemente invertir la dirección de la iteración suele ser una solución más simple y elegante .
  • De manera similar, al modificar o eliminar elementos DOM anidados , el procesamiento a la inversa puede evitar errores . Por ejemplo, considere modificar el innerHTML de un nodo padre antes de manejar sus hijos. En el momento en que se alcance el nodo hijo, se separará del DOM, habiendo sido reemplazado por un hijo recién creado cuando se escribió el innerHTML del padre.
  • Es más corto de escribir y leer que algunas de las otras opciones disponibles. Aunque pierde con forEach()y con ES6 for ... of.

Desventajas:

  • Procesa los artículos en orden inverso. Si estaba creando una nueva matriz a partir de los resultados o imprimiendo cosas en la pantalla, naturalmente la salida se invertirá con respecto al orden original.
  • La inserción repetida de hermanos en el DOM como primer hijo para mantener su orden es menos eficiente . (El navegador seguiría teniendo que cambiar las cosas correctamente). Para crear nodos DOM de manera eficiente y en orden, simplemente haga un bucle hacia adelante y anexe como de costumbre (y también use un "fragmento de documento").
  • El ciclo inverso es confuso para los desarrolladores junior. (Puede considerar eso una ventaja, dependiendo de su perspectiva).

¿Debería usarlo siempre?

Algunos desarrolladores usan el bucle for inverso de forma predeterminada , a menos que haya una buena razón para hacerlo hacia adelante.

Aunque las ganancias de rendimiento suelen ser insignificantes, es una especie de gritos:

"Just do this to every item in the list, I don't care about the order!"

Sin embargo, en la práctica, eso no es realmente una indicación confiable de intención, ya que es indistinguible de aquellas ocasiones en las que sí se preocupa por el orden y realmente necesita hacer un ciclo inverso. Entonces, de hecho, se necesitaría otra construcción para expresar con precisión la intención "no me importa", algo que actualmente no está disponible en la mayoría de los idiomas, incluido ECMAScript, pero que podría llamarse, por ejemplo forEachUnordered(),.

Si el orden no importa y la eficiencia es una preocupación (en el bucle más interno de un juego o motor de animación), entonces puede ser aceptable usar el bucle for inverso como patrón de referencia. ¡Solo recuerde que ver un bucle for inverso en el código existente no significa necesariamente que el orden sea irrelevante!

Era mejor usar forEach ()

En general, para el código de nivel superior donde la claridad y la seguridad son mayores preocupaciones, anteriormente recomendé usarlo Array::forEachcomo patrón predeterminado para bucles (aunque en estos días prefiero usarlo for..of). Las razones para preferir forEachun bucle inverso son:

  • Es más claro de leer.
  • Indica que i no se desplazará dentro del bloque (lo que siempre es una posible sorpresa que se esconde en largos fory whilebucles).
  • Le da un margen libre para los cierres.
  • Reduce la fuga de variables locales y la colisión accidental con (y mutación de) variables externas.

Luego, cuando vea el bucle for inverso en su código, es una pista de que se invierte por una buena razón (quizás una de las razones descritas anteriormente). Y ver un bucle for hacia adelante tradicional puede indicar que se pueden producir cambios.

(Si la discusión sobre la intención no tiene sentido para usted, entonces usted y su código pueden beneficiarse al ver la conferencia de Crockford sobre Estilo de programación y su cerebro ).

Ahora es incluso mejor usarlo para ... ¡de!

Existe un debate sobre si son preferibles for..ofo forEach()son:

  • Para una compatibilidad máxima con el navegador, se for..of requiere un polyfill para iteradores, lo que hace que su aplicación sea un poco más lenta de ejecutar y un poco más grande de descargar.

  • Por esa razón (y para fomentar el uso de mapy filter), ¡ algunas guías de estilo de front-end prohíben por for..ofcompleto!

  • Pero las preocupaciones anteriores no se aplican a las aplicaciones de Node.js, donde for..ofahora tiene un buen soporte.

  • Y además await no funciona por dentro forEach(). El uso for..ofes el patrón más claro en este caso.

Personalmente, tiendo a usar lo que parece más fácil de leer, a menos que el rendimiento o la minificación se hayan convertido en una preocupación importante. Así que en estos días prefiero usar en for..oflugar de forEach(), pero siempre usaré mapo filtero findo somecuando corresponda. (Por el bien de mis colegas, rara vez lo uso reduce).


¿Como funciona?

for (var i = 0; i < array.length; i++) { ... }   // Forwards

for (var i = array.length; i--; )    { ... }   // Reverse

Notarás que i--es la cláusula del medio (donde solemos ver una comparación) y la última cláusula está vacía (donde solemos ver i++). Eso significa que i--también se utiliza como condición para la continuación. Fundamentalmente, se ejecuta y se comprueba antes de cada iteración.

  • ¿Cómo puede empezar array.lengthsin explotar?

    Debido a que se i--ejecuta antes de cada iteración, en la primera iteración estaremos accediendo al elemento en el array.length - 1que se evita cualquier problema con los elementos fuera de límites de Array undefined .

  • ¿Por qué no deja de iterar antes del índice 0?

    El ciclo dejará de iterarse cuando la condición se i--evalúe a un valor falsey (cuando arroja 0).

    El truco es que --i, a diferencia de lo que sucede , el i--operador de seguimiento disminuye ipero produce el valor antes del decremento. Su consola puede demostrar esto:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    Entonces, en la iteración final, i era previamente 1 y la i--expresión lo cambia a 0, pero en realidad da 1 (veraz), por lo que la condición pasa. En la siguiente iteración i--cambia i a -1 pero produce 0 (falsey), lo que hace que la ejecución salga inmediatamente de la parte inferior del ciclo.

    En los tradicionales forwards for loop, i++y ++ison intercambiables (como señala Douglas Crockford). Sin embargo, en el bucle for inverso, debido a que nuestro decremento también es nuestra expresión de condición, debemos seguir i--si queremos procesar el elemento en el índice 0.


Trivialidades

A algunas personas les gusta dibujar una pequeña flecha en el forbucle inverso y terminar con un guiño:

for (var i = array.length; i --> 0 ;) {

Los créditos van a WYL por mostrarme los beneficios y los horrores del reverso para bucle.

0
92

Algunos lenguajes de estilo C utilizan foreachpara recorrer enumeraciones. En JavaScript, esto se hace con la for..inestructura de bucle :

var index,
    value;
for (index in obj) {
    value = obj[index];
}

Hay una trampa. for..inrecorrerá cada uno de los miembros enumerables del objeto y los miembros de su prototipo. Para evitar leer valores que se heredan a través del prototipo del objeto, simplemente verifique si la propiedad pertenece al objeto:

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

Además, ECMAScript 5 ha agregado un forEachmétodo al Array.prototypeque se puede usar para enumerar una matriz usando un calback (el polyfill está en los documentos, por lo que aún puede usarlo para navegadores más antiguos):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

Es importante tener en cuenta que Array.prototype.forEachno se rompe cuando regresa la devolución de llamada false. jQuery y Underscore.js proporcionan sus propias variaciones eachpara proporcionar bucles que pueden cortocircuitarse.

0
52

Si desea hacer un bucle sobre una matriz, use el forbucle estándar de tres partes .

for (var i = 0; i < myArray.length; i++) {
    var arrayItem = myArray[i];
}

Puede obtener algunas optimizaciones de rendimiento almacenando en caché myArray.lengtho iterando hacia atrás.

0
39

Si no le importa vaciar la matriz:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

xcontendrá el último valor de yy se eliminará de la matriz. También puede usar shift()cuál dará y quitará el primer elemento de y.

0
38

Sé que esta es una publicación antigua y ya hay muchas respuestas geniales. Para completar un poco más, pensé que agregaría otro usando AngularJS . Por supuesto, esto solo se aplica si está usando Angular, obviamente, no obstante, me gustaría decirlo de todos modos.

angular.forEachtoma 2 argumentos y un tercer argumento opcional. El primer argumento es el objeto (matriz) sobre el que iterar, el segundo argumento es la función de iterador y el tercer argumento opcional es el contexto del objeto (básicamente al que se hace referencia dentro del bucle como 'esto'.

Hay diferentes formas de usar el bucle forEach de angular. El más simple y probablemente el más utilizado es

var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
    //item will be each element in the array
    //do something
});

Otra forma que es útil para copiar elementos de una matriz a otra es

var temp = [1, 2, 3];
var temp2 = [];
angular.forEach(temp, function(item) {
    this.push(item); //"this" refers to the array passed into the optional third parameter so, in this case, temp2.
}, temp2);

Sin embargo, no tiene que hacer eso, simplemente puede hacer lo siguiente y es equivalente al ejemplo anterior:

angular.forEach(temp, function(item) {
    temp2.push(item);
});

Ahora existen pros y contras de usar la angular.forEachfunción en lugar del forbucle integrado con sabor a vainilla .

Pros

  • Fácil legibilidad
  • Fácil escritura
  • Si está disponible, angular.forEachutilizará el ciclo ES5 forEach. Ahora, llegaré a la eficiencia en la sección de contras, ya que los bucles forEach son mucho más lentos que los bucles for. Menciono esto como un profesional porque es bueno ser consistente y estandarizado.

Considere los siguientes 2 bucles anidados, que hacen exactamente lo mismo. Digamos que tenemos 2 matrices de objetos y cada objeto contiene una matriz de resultados, cada uno de los cuales tiene una propiedad Value que es una cadena (o lo que sea). Y digamos que tenemos que iterar sobre cada uno de los resultados y, si son iguales, realizar alguna acción:

angular.forEach(obj1.results, function(result1) {
    angular.forEach(obj2.results, function(result2) {
        if (result1.Value === result2.Value) {
            //do something
        }
    });
});

//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
    for (var j = 0; j < obj2.results.length; j++) {
        if (obj1.results[i].Value === obj2.results[j].Value) {
            //do something
        }
    }
}

De acuerdo, este es un ejemplo hipotético muy simple, pero he escrito bucles for incrustados triples utilizando el segundo enfoque y fue muy difícil de leer y escribir.

Contras

  • Eficiencia. angular.forEach, y el nativo forEach, para el caso, son mucho más lentos que el forbucle normal ... aproximadamente un 90% más lento . Entonces, para conjuntos de datos grandes, es mejor ceñirse al forciclo nativo .
  • No se interrumpirá, continuará o devolverá el soporte. continueen realidad está soportado por " accidente ", para continuar en un angular.forEachsimple, ponga una return;declaración en la función como lo angular.forEach(array, function(item) { if (someConditionIsTrue) return; });que hará que continúe fuera de la función para esa iteración. Esto también se debe al hecho de que el nativo forEachtampoco admite romper o continuar.

Estoy seguro de que también hay otros pros y contras, y siéntase libre de agregar los que considere adecuados. Creo que, en resumen, si necesita eficiencia, quédese con el forbucle nativo para sus necesidades de bucle. Pero, si sus conjuntos de datos son más pequeños y está bien renunciar a cierta eficiencia a cambio de legibilidad y escritura, entonces, por supuesto, eche un vistazo angular.forEacha ese chico malo.

36

Una implementación de forEach ( ver en jsFiddle ):

function forEach(list,callback) {
  var length = list.length;
  for (var n = 0; n < length; n++) {
    callback.call(list[n]);
  }
}

var myArray = ['hello','world'];

forEach(
  myArray,
  function(){
    alert(this); // do something
  }
);
0
34

A partir de ECMAScript 6:

list = [0, 1, 2, 3]
for (let obj of list) {
    console.log(obj)
}

Donde ofevita las rarezas asociadas con iny hace que funcione como el forbucle de cualquier otro idioma, y ​​se letune identro del bucle en lugar de dentro de la función.

Las llaves ( {}) se pueden omitir cuando solo hay un comando (por ejemplo, en el ejemplo anterior).

31

Probablemente el for(i = 0; i < array.length; i++)bucle no sea la mejor opción. ¿Por qué? Si tienes esto:

var array = new Array();
array[1] = "Hello";
array[7] = "World";
array[11] = "!";

El método llamará de array[0]a array[2]. Primero, esto primero hará referencia a variables que ni siquiera tiene, segundo, no tendría las variables en la matriz, y tercero, hará que el código sea más audaz. Mira aquí, es lo que uso:

for(var i in array){
    var el = array[i];
    //If you want 'i' to be INT just put parseInt(i)
    //Do something with el
}

Y si quieres que sea una función, puedes hacer esto:

function foreach(array, call){
    for(var i in array){
        call(array[i]);
    }
}

Si quieres romper, un poco más de lógica:

function foreach(array, call){
    for(var i in array){
        if(call(array[i]) == false){
            break;
        }
    }
}

Ejemplo:

foreach(array, function(el){
    if(el != "!"){
        console.log(el);
    } else {
        console.log(el+"!!");
    }
});

Vuelve:

//Hello
//World
//!!!
0
31

Hay tres implementaciones de foreachen jQuery de la siguiente manera.

var a = [3,2];

$(a).each(function(){console.log(this.valueOf())}); //Method 1
$.each(a, function(){console.log(this.valueOf())}); //Method 2
$.each($(a), function(){console.log(this.valueOf())}); //Method 3
0
30

Una solución fácil ahora sería usar la biblioteca underscore.js . Proporciona muchas herramientas útiles, como eachy delegará automáticamente el trabajo al nativo forEachsi está disponible.

Un ejemplo de CodePen de cómo funciona es:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

Ver también

27

No hay ningún for eachbucle en JavaScript nativo . Puede usar bibliotecas para obtener esta funcionalidad (recomiendo Underscore.js ), use un forbucle in simple .

for (var instance in objects) {
   ...
}

Sin embargo, tenga en cuenta que puede haber razones para usar un forbucle aún más simple (consulte la pregunta de Stack Overflow ¿ Por qué usar "for ... in" con la iteración de matriz es una mala idea? )

var instance;
for (var i=0; i < objects.length; i++) {
    var instance = objects[i];
    ...
}
24

Este es un iterador para una lista NO dispersa donde el índice comienza en 0, que es el escenario típico cuando se trata de document.getElementsByTagName o document.querySelectorAll)

function each( fn, data ) {

    if(typeof fn == 'string')
        eval('fn = function(data, i){' + fn + '}');

    for(var i=0, L=this.length; i < L; i++) 
        fn.call( this[i], data, i );   

    return this;
}

Array.prototype.each = each;  

Ejemplos de uso:

Ejemplo 1

var arr = [];
[1, 2, 3].each( function(a){ a.push( this * this}, arr);
arr = [1, 4, 9]

Ejemplo # 2

each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');

Cada etiqueta p obtiene class="blue"

Ejemplo # 3

each.call(document.getElementsByTagName('p'), 
    "if( i % 2 == 0) this.className = data;",
    'red'
);

Cada otra etiqueta p obtiene class="red">

Ejemplo # 4

each.call(document.querySelectorAll('p.blue'), 
    function(newClass, i) {
        if( i < 20 )
            this.className = newClass;
    }, 'green'
);

Y finalmente las primeras 20 etiquetas p azules se cambian a verde

Precaución al usar una cadena como función: la función se crea fuera de contexto y debe usarse solo cuando esté seguro del alcance de la variable. De lo contrario, es mejor pasar funciones donde el alcance es más intuitivo.

23

Hay algunas formas de recorrer una matriz en JavaScript, como se muestra a continuación:

porque es el más común . Bloque completo de código para bucle

var languages = ["Java", "JavaScript", "C#", "Python"];
var i, len, text;
for (i = 0, len = languages.length, text = ""; i < len; i++) {
    text += languages[i] + "<br>";
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

while - bucle mientras se completa una condición. Parece ser el bucle más rápido

var text = "";
var i = 0;
while (i < 10) {
    text +=  i + ") something<br>";
    i++;
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

do / while - también recorre un bloque de código mientras la condición es verdadera, se ejecutará al menos una vez

var text = ""
var i = 0;

do {
    text += i + ") something <br>";
    i++;
}
while (i < 10);

document.getElementById("example").innerHTML = text;
<p id="example"></p>

Bucles funcionales - forEach, map, filter, también reduce(que bucle a través de la función, pero se utilizan si hay que hacer algo con su matriz, etc.

// For example, in this case we loop through the number and double them up using the map function
var numbers = [65, 44, 12, 4];
document.getElementById("example").innerHTML = numbers.map(function(num){return num * 2});
<p id="example"></p>

Para obtener más información y ejemplos sobre la programación funcional en matrices, consulte la publicación del blog Programación funcional en JavaScript: mapear, filtrar y reducir .

0
22

ECMAScript 5 (la versión en JavaScript) para trabajar con matrices:

forEach : repite todos los elementos de la matriz y hace lo que sea necesario con cada elemento.

['C', 'D', 'E'].forEach(function(element, index) {
  console.log(element + " is #" + (index+1) + " in the musical scale");
});

// Output
// C is the #1 in musical scale
// D is the #2 in musical scale
// E is the #3 in musical scale

En caso de que esté más interesado en la operación en una matriz utilizando alguna característica incorporada.

map : crea una nueva matriz con el resultado de la función de devolución de llamada. Este método es bueno para usarlo cuando necesite formatear los elementos de su matriz.

// Let's upper case the items in the array
['bob', 'joe', 'jen'].map(function(elem) {
  return elem.toUpperCase();
});

// Output: ['BOB', 'JOE', 'JEN']

reduce - Como dice el nombre, reduce la matriz a un solo valor llamando a la función dada pasando el elemento actual y el resultado de la ejecución anterior.

[1,2,3,4].reduce(function(previous, current) {
  return previous + current;
});
// Output: 10
// 1st iteration: previous=1, current=2 => result=3
// 2nd iteration: previous=3, current=3 => result=6
// 3rd iteration: previous=6, current=4 => result=10

cada : devuelve verdadero o falso si todos los elementos de la matriz pasan la prueba en la función de devolución de llamada.

// Check if everybody has 18 years old of more.
var ages = [30, 43, 18, 5];
ages.every(function(elem) {
  return elem >= 18;
});

// Output: false

filtro : muy similar a todos, excepto que el filtro devuelve una matriz con los elementos que devuelven verdaderos a la función dada.

// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
  return (elem % 2 == 0)
});

// Output: [2,4,6]
19

No hay capacidad incorporada para entrar forEach. Para interrumpir la ejecución, use lo Array#somesiguiente:

[1,2,3].some(function(number) {
    return number === 1;
});

Esto funciona porque somedevuelve verdadero tan pronto como cualquiera de las devoluciones de llamada, ejecutadas en orden de matriz, devuelve verdadero, cortocircuitando la ejecución del resto. Respuesta original ver prototipo de matriz para algunos

18

También me gustaría agregar esto como una composición de un bucle inverso y una respuesta anterior para alguien a quien también le gustaría esta sintaxis.

var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
    console.log(item);
}

Pros:

El beneficio de esto: ya tiene la referencia en el primero, por lo que no será necesario declararla más tarde con otra línea. Es útil cuando se recorre la matriz de objetos.

Contras:

Esto se interrumpirá siempre que la referencia sea falsa: falsey (indefinido, etc.). Sin embargo, puede usarse como una ventaja. Sin embargo, lo haría un poco más difícil de leer. Y también, dependiendo del navegador, puede "no" estar optimizado para funcionar más rápido que el original.

12

forma jQuery usando $.map:

var data = [1, 2, 3, 4, 5, 6, 7];

var newData = $.map(data, function(element) {
    if (element % 2 == 0) {
        return element;
    }
});

// newData = [2, 4, 6];
0
10

Uso de bucles con la desestructuración de ECMAScript 6 y el operador de propagación

La desestructuración y el uso del operador de propagación han demostrado ser bastante útiles para los recién llegados a ECMAScript 6 por ser más estético / legible por humanos, aunque algunos veteranos de JavaScript pueden considerarlo complicado. Los jóvenes o algunas otras personas pueden encontrarlo útil.

The following examples will use the for...of statement and the .forEach method.

Examples 6, 7, and 8 can be used with any functional loops like .map, .filter, .reduce, .sort, .every, .some. For more information about these methods, check out the Array Object.

Ejemplo 1:for...of bucle normal : aquí no hay trucos.

let arrSimple = ['a', 'b', 'c'];

for (let letter of arrSimple) {
  console.log(letter);
}

Ejemplo 2: dividir palabras en caracteres

let arrFruits = ['apple', 'orange', 'banana'];

for (let [firstLetter, ...restOfTheWord] of arrFruits) {
  // Create a shallow copy using the spread operator
  let [lastLetter] = [...restOfTheWord].reverse();
  console.log(firstLetter, lastLetter, restOfTheWord);
}

Ejemplo 3: Bucle con una keyyvalue

// let arrSimple = ['a', 'b', 'c'];

// Instead of keeping an index in `i` as per example `for(let i = 0 ; i<arrSimple.length;i++)`
// this example will use a multi-dimensional array of the following format type:
// `arrWithIndex: [number, string][]`

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Same thing can be achieved using `.map` method
// let arrWithIndex = arrSimple.map((i, idx) => [idx, i]);

// Same thing can be achieved using `Object.entries`
// NOTE: `Object.entries` method doesn't work on Internet Explorer  unless it's polyfilled
// let arrWithIndex = Object.entries(arrSimple);

for (let [key, value] of arrWithIndex) {
  console.log(key, value);
}

Ejemplo 4: obtener propiedades de objeto en línea

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];

for (let { name, age: aliasForAge } of arrWithObjects) {
  console.log(name, aliasForAge);
}

Ejemplo 5: Obtenga propiedades de objetos profundos de lo que necesita

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

for (let { name, tags: [firstItemFromTags, ...restOfTags] } of arrWithObjectsWithArr) {
  console.log(name, firstItemFromTags, restOfTags);
}

Ejemplo 6: ¿Se usa el Ejemplo 3 con.forEach

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Not to be confused here, `forEachIndex` is the real index
// `mappedIndex` was created by "another user", so you can't really trust it

arrWithIndex.forEach(([mappedIndex, item], forEachIndex) => {
  console.log(forEachIndex, mappedIndex, item);
});

Ejemplo 7: ¿Se usa el Ejemplo 4 con.forEach

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];
// NOTE: Destructuring objects while using shorthand functions
// are required to be surrounded by parentheses
arrWithObjects.forEach( ({ name, age: aliasForAge }) => {
  console.log(name, aliasForAge)
});

Ejemplo 8: ¿ Se usa el Ejemplo 5 con.forEach

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

arrWithObjectsWithArr.forEach(({
  name,
  tags: [firstItemFromTags, ...restOfTags]
}) => {
  console.log(name, firstItemFromTags, restOfTags);
});
9

Rendimiento

Hoy (18/12/2019) realizo prueba en mi v10.13.6 macOS (High Sierra), en Chrome v 79.0, v13.0.4 Safari y Firefox v71.0 (64 bits) - Conclusiones sobre la optimización (y micro-optimización de los cuales generalmente no vale la pena introducirlo en el código porque el beneficio es pequeño, pero la complejidad del código aumenta).

  • Parece que el tradicional for i( Aa ) es una buena opción para escribir código rápido en todos los navegadores.

  • Las otras soluciones, como for-of( Ad ), todas en el grupo C. ... suelen ser de 2 a 10 (y más) más lentas que Aa , pero para arreglos pequeños está bien usarla, en aras de aumentar la claridad del código.

  • Los bucles con una longitud de matriz almacenada en caché n( Ab, Bb, Be ) a veces son más rápidos, a veces no. Probablemente los compiladores detectan automáticamente esta situación e introducen el almacenamiento en caché. Las diferencias de velocidad entre las versiones en caché y sin caché ( Aa, Ba, Bd ) son aproximadamente ~ 1%, por lo que parece que la introducción nes una microoptimización .

  • Las i--soluciones similares en las que el bucle comienza desde el último elemento de la matriz ( Ac, Bc ) suelen ser ~ 30% más lentas que las soluciones directas; probablemente la razón sea la forma en que funciona la memoria caché de la CPU ; la lectura de la memoria directa es más óptima para la memoria caché de la CPU). Se recomienda NO UTILIZAR este tipo de soluciones.

Detalles

En las pruebas calculamos la suma de los elementos de la matriz. Realizo una prueba para matrices pequeñas (10 elementos) y matrices grandes (elementos de 1M) y las divido en tres grupos:

  • A - forpruebas
  • B - whilepruebas
  • C - otros métodos / alternativos

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//let arr = Array.from(Array(1000000), (x, i) => i%10);

function Aa(a, s=0) {
  for(let i=0; i<a.length; i++) {
    s += a[i];
  }
  console.log('Aa=', s);
}

function Ab(a, s=0) {
  let n = a.length;
  for(let i=0; i<n; i++) {
    s += a[i];
  }
  console.log('Ab=', s);
}

function Ac(a, s=0) {
  for(let i=a.length; i--;) {
    s += a[i];
  }
  console.log('Ac=', s);
}

function Ad(a, s=0) {
  for(let x of a) {
    s += x;
  }
  console.log('Ad=', s);
}

function Ae(a, s=0) {
  for(let i in a) if (a.hasOwnProperty(i)) {
    s += a[i];
  }
  console.log('Ae=', s);
}

function Ba(a, s=0) {
  let i = -1;
  while(++i < a.length) {
    s+= a[i];
  }
  console.log('Ba=', s);
}

function Bb(a, s=0) {
  let i = -1;
  let n = a.length;
  while(++i < n) {
    s+= a[i];
  }
  console.log('Bb=', s);
}

function Bc(a, s=0) {
  let i = a.length;
  while(i--) {
    s += a[i];
  }
  console.log('Bc=', s);
}

function Bd(a, s=0) {
  let i = 0;
  do {
    s+= a[i]
  } while (++i < a.length);
  console.log('Bd=', s);
}

function Be(a, s=0) {
  let i = 0;
  let n = a.length;
  do {
    s += a[i]
  } while (++i < n);
  console.log('Be=', s);
}

function Bf(a, s=0) {
  const it = a.values(); 
  let e;
  while (!(e = it.next()).done) { 
    s+= e.value; 
  }
  console.log('Bf=', s);
}

function Ca(a, s=0) {
  a.map(x => { s+=x });
  console.log('Ca=', s);
}

function Cb(a, s=0) {
  a.forEach(x => { s+=x });
  console.log('Cb=', s);
}

function Cc(a, s=0) {
  a.every(x => (s += x, 1));
  console.log('Cc=', s);
}

function Cd(a, s=0) {
  a.filter(x => { s+=x });
  console.log('Cd=',s);
}

function Ce(a, s=0) {
  a.reduce((z, c) => { s+=c }, 0);
  console.log('Ce=', s);
}

function Cf(a, s=0) {
  a.reduceRight((z, c) => { s += c }, 0);
  console.log('Cf=', s);
}

function Cg(a, s=0) {
  a.some(x => { s += x } );
  console.log('Cg=', s);
}

function Ch(a, s=0) {
  Array.from(a, x=> s += x);
  console.log('Cc=', s);
}


Aa(arr);
Ab(arr);
Ac(arr);
Ad(arr);
Ae(arr);

Ba(arr);
Bb(arr);
Bc(arr);
Bd(arr);
Be(arr);
Bf(arr);

Ca(arr);
Cb(arr);
Cc(arr);
Cd(arr);
Ce(arr);
Cf(arr);
Cg(arr);
Ch(arr);
<p style="color: red">This snippets only PRESENTS code used for benchmark - it not perform test itself</p>

Resultados de varios navegadores

Resultados para todos los navegadores probados

Ingrese la descripción de la imagen aquínavegadores **

Matriz con 10 elementos

Resultados para Chrome. Puede realizar la prueba en su máquina aquí .

Ingrese la descripción de la imagen aquí

Matriz con 1,000,000 de elementos

Resultados para Chrome. Puede realizar la prueba en su máquina aquí

Ingrese la descripción de la imagen aquí

8

Una forma más cercana a su idea sería usar Array.forEach()que acepte una función de cierre que se ejecutará para cada elemento de la matriz.

myArray.forEach(
  (item) => {
    // Do something
    console.log(item);
  }
);

Otra forma viable sería usar el Array.map()que funciona de la misma manera, pero también toma todos los valores que devuelve y los devuelve en una nueva matriz (esencialmente asignando cada elemento a uno nuevo), así:

var myArray = [1, 2, 3];
myArray = myArray.map(
  (item) => {
    return item + 1;
  }
);

console.log(myArray); // [2, 3, 4]
0
7

La sintaxis lambda no suele funcionar en Internet Explorer 10 o versiones anteriores.

Yo suelo usar el

[].forEach.call(arrayName,function(value,index){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});

Si eres un fan de jQuery y ya tienes un archivo jQuery en ejecución, debes invertir las posiciones de los parámetros de índice y valor.

$("#ul>li").each(function(**index, value**){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});
7

Puedes llamar a cada uno así:

forEachiterará sobre la matriz que proporcione y para cada iteración tendrá elementque contiene el valor de esa iteración. Si necesita un índice, puede obtener el índice actual pasando icomo segundo parámetro en la función de devolución de llamada para forEach.

Foreach es básicamente una función de orden superior, que toma otra función como parámetro.

let theArray= [1,3,2];

theArray.forEach((element) => {
  // Use the element of the array
  console.log(element)
}

Producción:

1
3
2

También puede iterar sobre una matriz como esta:

for (let i=0; i<theArray.length; i++) {
  console.log(i); // i will have the value of each index
}
7

Según la nueva función actualizada ECMAScript 6 (ES6) y ECMAScript 2015, puede utilizar las siguientes opciones con bucles:

for loops

for(var i = 0; i < 5; i++){
  console.log(i);
}

// Output: 0,1,2,3,4

for...in loops

let obj = {"a":1, "b":2}

for(let k in obj){
  console.log(k)
}

// Output: a,b

Array.forEach()

let array = [1,2,3,4]

array.forEach((x) => {
  console.log(x);
})

// Output: 1,2,3,4

for...of loops

let array = [1,2,3,4]

for(let x of array){
  console.log(x);
}

// Output: 1,2,3,4

while loops

let x = 0

while(x < 5){
  console.log(x)
  x++
}

// Output: 1,2,3,4

do...while loops

let x = 0

do{
  console.log(x)
  x++
}while(x < 5)

// Output: 1,2,3,4
7

Resumen:

Al iterar sobre una matriz, a menudo queremos lograr uno de los siguientes objetivos:

  1. Queremos iterar sobre la matriz y crear una nueva matriz:

    Array.prototype.map

  2. Queremos iterar sobre la matriz y no crear una nueva matriz:

    Array.prototype.forEach

    for..of círculo

En JavaScript, hay muchas formas de lograr ambos objetivos. Sin embargo, algunos son más convenientes que otros. A continuación, puede encontrar algunos métodos de uso común (el IMO más conveniente) para lograr la iteración de matrices en JavaScript.

Creando una nueva matriz: Map

map()es una función ubicada en la Array.prototypeque puede transformar cada elemento de una matriz y luego devuelve una nueva matriz. map()toma como argumento una función de devolución de llamada y funciona de la siguiente manera:

let arr = [1, 2, 3, 4, 5];

let newArr = arr.map((element, index, array) => {
  return element * 2;
})

console.log(arr);
console.log(newArr);

La devolución de llamada a la que hemos pasado map()como argumento se ejecuta para cada elemento. Luego se devuelve una matriz que tiene la misma longitud que la matriz original. En esta nueva matriz, el elemento es transformado por la función de devolución de llamada pasada como argumento a map().

La diferencia distintiva entre mapotro mecanismo de bucle como forEachy un for..ofbucle es que mapdevuelve una nueva matriz y deja la matriz anterior intacta (excepto si la manipula explícitamente con piensa como splice).

Además, tenga en cuenta que la mapdevolución de llamada de la función proporciona el número de índice de la iteración actual como segundo argumento. Además, ¿el tercer argumento proporciona la matriz en la que mapse llamó? A veces, estas propiedades pueden resultar muy útiles.

Bucle usando forEach

forEaches una función que se encuentra en la Array.prototypeque toma una función de devolución de llamada como argumento. Luego ejecuta esta función de devolución de llamada para cada elemento de la matriz. A diferencia de la map()función, la función forEach no devuelve nada ( undefined). Por ejemplo:

let arr = [1, 2, 3, 4, 5];

arr.forEach((element, index, array) => {

  console.log(element * 2);

  if (index === 4) {
    console.log(array)
  }
  // index, and oldArray are provided as 2nd and 3th argument by the callback

})

console.log(arr);

Al igual que la mapfunción, la forEachdevolución de llamada proporciona el número de índice de la iteración actual como segundo argumento. Además, ¿el tercer argumento proporciona la matriz en la que forEachse llamó?

Bucle a través de elementos usando for..of

El for..ofbucle recorre cada elemento de una matriz (o cualquier otro objeto iterable). Funciona de la siguiente manera:

let arr = [1, 2, 3, 4, 5];

for(let element of arr) {
  console.log(element * 2);
}

En el ejemplo anterior, elementrepresenta un elemento de matriz y arres la matriz que queremos repetir. Tenga en cuenta que el nombre elementes arbitrario y podríamos haber elegido cualquier otro nombre como 'el' o algo más declarativo cuando sea aplicable.

No confunda el for..inbucle con el for..ofbucle. for..inrecorrerá todas las propiedades enumerables de la matriz, mientras que el for..ofbucle solo recorrerá los elementos de la matriz. Por ejemplo:

let arr = [1, 2, 3, 4, 5];

arr.foo = 'foo';

for(let element of arr) {
  console.log(element);
}

for(let element in arr) {
  console.log(element);
}
6

Si desea usar forEach(), se verá así:

theArray.forEach ( element => {
    console.log(element);
});

Si desea usar for(), se verá así:

for(let idx = 0; idx < theArray.length; idx++){
    let element = theArray[idx];
    console.log(element);
}
6

Si desea recorrer una matriz de objetos con la función de flecha:

let arr = [{name:'john', age:50}, {name:'clark', age:19}, {name:'mohan', age:26}];

arr.forEach((person)=>{
  console.log('I am ' + person.name + ' and I am ' + person.age + ' old');
})
5

Si tiene una matriz masiva, debería usarla iteratorspara ganar algo de eficiencia. Los iteradores son una propiedad de ciertas colecciones de JavaScript (como Map, Set, String, Array). Incluso, for..ofutiliza iteratorbajo el capó.

Los iteradores mejoran la eficiencia al permitirle consumir los elementos de una lista de uno en uno como si fueran un flujo. Lo que hace que un iterador sea especial es cómo atraviesa una colección. Otros bucles necesitan cargar la colección completa por adelantado para iterar sobre ella, mientras que un iterador solo necesita conocer la posición actual en la colección.

Puede acceder al elemento actual llamando al nextmétodo del iterador . El siguiente método devolverá el valuedel elemento actual y un booleanpara indicar cuándo ha llegado al final de la colección. El siguiente es un ejemplo de cómo crear un iterador a partir de una matriz.

Transforme su matriz regular en iterador usando un values()método como este:

    const myArr = [2,3,4]

let it = myArr.values();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

También puede transformar su matriz regular en iterador usando Symbol.iteratorasí:

const myArr = [2,3,4]

let it = myArr[Symbol.iterator]();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

También puede transformar su habitual arrayen uno iteratorcomo este:

let myArr = [8, 10, 12];

function makeIterator(array) {
    var nextIndex = 0;
    
    return {
       next: function() {
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    };
};

var it = makeIterator(myArr);

console.log(it.next().value);   // {value: 8, done: false}
console.log(it.next().value);   // {value: 10, done: false}
console.log(it.next().value);   // {value: 12, done: false}
console.log(it.next().value);   // {value: undefined, done: true}

NOTA :

  • Los iteradores son agotables por naturaleza.
  • Los objetos no son iterablepor defecto. Úselo for..inen ese caso porque en lugar de valores funciona con claves.

Puedes leer más sobre iteration protocol aquí .