JavaScript para ... en vs para

461

¿Crees que hay una gran diferencia en ... en y para bucles? ¿Qué tipo de "para" prefiere usar y por qué?

Digamos que tenemos una matriz de matrices asociativas:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Entonces podemos iterar:

for (var i = 0; i < myArray.length; i++)

Y:

for (var i in myArray)

No veo una gran diferencia. ¿Hay algún problema de rendimiento?

andrii
fuente
13
Tenga en cuenta que también nosotros, como de JS 1.6 , tenemos: myArray.forEach(callback[, thisarg]).
Benji XVI
14
@Benji array.forEach está realmente en ES5.
mikemaccana
2
en un bucle for-in necesita un condicional que se vea así:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky
66
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); está bien usar casi en todas partes, excepto en IE8, y es, con mucho, la sintaxis más elegante
Jon z
55
También hay una for...ofdeclaración en ECMAScript 6 , por ejemplo:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Respuestas:

548

La elección debe basarse en qué idioma se entiende mejor.

Una matriz se itera usando:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

Un objeto que se usa como una matriz asociativa se itera usando:

for (var key in o)
  //do stuff with o[key]

A menos que tenga razones devastadoras, manténgase en el patrón de uso establecido.

AnthonyWJones
fuente
38
Cabe mencionar que es una buena práctica usar para ... en la declaración de filtrado de if. Hay un método útil de Object "obj.hasOwnProperty (miembro)" que comprueba si un miembro devuelto por el iterador es realmente miembro del objeto. Ver: javascript.crockford.com/code.html
Damir Zekić
57
Como se comentó en otra (s) respuesta (s), "for ... in" no funciona correctamente para las matrices, ya que iterará sobre todas las propiedades y métodos de la matriz. Por lo tanto, debe usar "for ... in" solo para iterar sobre las propiedades del objeto. De lo contrario, quédese con "for (i = 0; i <something; i ++)"
Denilson Sá Maia
Por razones de rendimiento, en mi opinión, es mejor evaluar la longitud de la matriz antes de for, no evaluar una longitud cada vez en el bucle.
UpTheCreek
99
@UpTheCreek: Eso es definitivamente cierto cuando la matriz es de hecho algo devuelto por HTMLDOM, sin embargo, me pregunto qué tan grande debería ser una matriz estándar de JavaScript antes de que pueda ver una diferencia apreciable. Personalmente, mantendría el código lo más simple posible hasta que se demuestre que es necesario hacer algo diferente.
AnthonyWJones
2
@ Pichan Creo que te refieres i < l, no i < a, en tu condición de bucle for.
Max Nanasy
161

Douglas Crockford recomienda en JavaScript: The Good Parts (página 24) para evitar el uso de la for indeclaración.

Si utiliza for inpara recorrer los nombres de propiedad en un objeto, los resultados no están ordenados. Peor: puede obtener resultados inesperados; incluye miembros heredados de la cadena del prototipo y el nombre de los métodos.

Todo menos las propiedades se pueden filtrar con .hasOwnProperty. Este ejemplo de código hace lo que probablemente quería originalmente:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}
Benno Richters
fuente
70
for ... in es perfectamente apropiado para recorrer las propiedades del objeto. No es apropiado para recorrer los elementos de la matriz. Si no puede entender la diferencia entre estos escenarios, entonces sí, debe evitar para ... en; de lo contrario, enloquece.
Shog9
8
¡Quiero enfatizar el hecho de que NO SE PIDE! Esto podría ser un gran problema y sería un error difícil de atrapar.
Jason
44
+1 para "incluye miembros heredados de la cadena de prototipos y el nombre de los métodos". Te divertirás si alguien usa tu código con Prototype cargado (incluso si tu código realmente no lo usa), por ejemplo.
ijw
13
No olvide declarar la namevariable: de lo for(var name in object)...contrario, si ese código está dentro de una función, por ejemplo, la namevariable termina siendo una propiedad del objeto global (una asignación a un identificador no declarado lo hace), también en el nuevo ECMAScript 5 Modo estricto, ese código arrojará un ReferenceError.
CMS
66
@Nosredna: Hay un problema sobre el orden de iteración para Chrome, presentado por nada menos que John Resig, que está marcado como WontFix. code.google.com/p/chromium/issues/detail?id=883 . Incluso antes de Chrome, el orden de iteración no era el mismo en todos los navegadores si elimina y luego agrega una propiedad nuevamente. Además, IE 9 se parece mucho a Chrome (supuestamente para mejoras de velocidad). Entonces ... Por favor, deje de difundir información inexacta, sería muy ingenuo seguir dependiendo de ello.
Juan Mendes
62

FYI - Usuarios de jQuery


El each(callback)método de jQuery usa el for( ; ; )bucle por defecto, y for( in ) solo lo usará si la longitud es undefined.

Por lo tanto, diría que es seguro asumir el orden correcto al usar esta función.

Ejemplo :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

La desventaja de usar esto es que si está haciendo alguna lógica que no sea de UI, sus funciones serán menos portables a otros marcos. La each()función probablemente esté mejor reservada para su uso con los selectores jQuery y, de lo for( ; ; )contrario, sería aconsejable.


Jason
fuente
44
Siempre hay documentcloud.github.com/underscore que tiene _.each y muchas otras funciones útiles
w00t
1
¿también significa que si tengo una propiedad de longitud en mi objeto $ .each fallará? por ejemplo, x = {a: "1", b: "2", longitud: 3}.
Onur Topal
29

existen diferencias de rendimiento según el tipo de bucle que use y en qué navegador.

Por ejemplo:

for (var i = myArray.length-1; i >= 0; i--)

es casi el doble de rápido en algunos navegadores que:

for (var i = 0; i < myArray.length; i++)

Sin embargo, a menos que sus matrices sean ENORMES o las repita constantemente, todas son lo suficientemente rápidas. Dudo seriamente que el bucle de matriz sea un cuello de botella en su proyecto (o para cualquier otro proyecto)

Gene
fuente
55
¿Almacenar "myArray.length" en una variable antes del bucle hará que la diferencia de rendimiento desaparezca? Mi suposición es "sí".
Tomalak
3
No. 'myArray.length' es una propiedad, no un método en un objeto; no se realiza ningún cálculo para determinar su valor. Almacenar su valor en una variable no hará nada en absoluto.
Jason
3
Sí hay. Las propiedades no son variables; tienen código get / set.
ste
12
Tiendo a usarfor(var i = myArray.length; i--;)
Kevin
2
Código de claridad y legibilidad. No para lo que sucede que se ejecuta marginalmente más rápido en algunos navegadores cuando se usan matrices absurdamente masivas en el momento actual. Las optimizaciones pueden cambiar, pero la legibilidad de su código (o falta de ella) no lo hará. Escriba código que otros puedan seguir fácilmente y permita que los optimizadores se pongan al día a su debido tiempo.
Aroth
26

Tenga en cuenta que el método nativo Array.forEach ahora es ampliamente compatible .

Sam Dutton
fuente
2
¿Qué hace? ¿Tiene los problemas mencionados en otras publicaciones (bucle sobre propiedades en lugar de elementos de la matriz)? Además, como IE8 no lo admite, es un poco exagerado decir que es ampliamente compatible.
Rauni Lillemets
2
tanto como los usuarios de * nix lo desprecian, IE8 es sobre todo usuarios de sub-windows7. esa es una porción masiva del mercado de navegadores.
sbartell
2
@Rauni: entiendo su punto de vista, pero para los dispositivos de escritorio, el uso compartido del navegador IE es inferior al 40%, de acuerdo con en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table , y de mercados.hitslink.com/… y otros sitios, al menos el 8% de los navegadores son IE 9. En otras palabras, Array.forEach es compatible con alrededor del 70% de los navegadores de escritorio, por lo que no creo que 'ampliamente admitido' no sea razonable. No lo he comprobado, pero el soporte móvil (en los navegadores WebKit y Opera) puede ser aún mayor. Obviamente, hay variaciones considerables geográficamente.
Sam Dutton
1
Gracias por la actualización. Estoy de acuerdo en que podría decirse que está "ampliamente respaldado". El único problema es que si el usuario usa este método JS, él / ella todavía tiene que escribir un método de respaldo para el caso si no es compatible ..
Rauni Lillemets
1
@Rauni: puede usar es5-shim y es6-shim para proporcionar automáticamente métodos de copia de seguridad. github.com/es-shims/es5-shim
Michiel van der Blonk
24

Respuesta actualizada para la versión actual de 2012 de todos los principales navegadores: Chrome, Firefox, IE9, Safari y Opera admiten la matriz nativa de ES5. Para cada uno.

A menos que tenga alguna razón para admitir IE8 de forma nativa (teniendo en cuenta que se puede proporcionar ES5-shim o Chrome frame a estos usuarios, lo que proporcionará un entorno JS adecuado), es más sencillo usar la sintaxis adecuada del lenguaje:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

La documentación completa de array.forEach () está en MDN.

mikemaccana
fuente
1
Realmente debe documentar los parámetros de la devolución de llamada: primero el valor del elemento, segundo el índice del elemento, tercero la matriz que se atraviesa
sorteo
Escucho lo que dices, pero en este caso, simplificar demasiado oscurece toda la gama de posibilidades. Tener tanto el índice como el valor significa que puede servir como un reemplazo tanto para ... en y para cada ... en, con la ventaja de que no tiene que recordar qué itera sobre las claves o los valores.
empalagoso
1
@Cory: ES5 forEach se puede agregar a navegadores ES3 heredados con bastante facilidad. Menos código es mejor código.
mikemaccana
2
@nailer ¿Se puede usar esto en matrices y objetos indistintamente?
hitautodestruct
1
@hitautodestruct Es parte del prototipo de Array, no del Objeto. En general, en la comunidad en este momento, los objetos que no son de matriz todavía se repiten con 'for (var key in object) {}'.
mikemaccana
14

Los dos no son lo mismo cuando la matriz es escasa.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}
Mike Samuel
fuente
14

Usando forEach para saltear la cadena del prototipo

Solo un apéndice rápido a la respuesta de @ nailer anterior , usar forEach con Object.keys significa que puede evitar iterar sobre la cadena del prototipo sin tener que usar hasOwnProperty.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});
meloncolía
fuente
2
maldición, eso es astuto. Valió la pena leer otras 50 publicaciones para llegar a esto. obj = {"rosa": "patos", rojo: "gansos"}; Object.keys (obj) === ["rosa", "rojo"]
Orwellophile
14

En segundo lugar, creo que debe elegir el método de iteración según sus necesidades. Te sugeriría que en realidad nunca recorras el nativo Arraycon la for inestructura. Es mucho más lento y , como Chase Seibert señaló en este momento, no es compatible con el marco Prototype.

Hay un excelente punto de referencia en diferentes estilos de bucle que debe tener en cuenta si trabaja con JavaScript . No haga optimizaciones tempranas, pero debe mantener esas cosas en algún lugar detrás de su cabeza.

Lo usaría for inpara obtener todas las propiedades de un objeto, lo que es especialmente útil al depurar sus scripts. Por ejemplo, me gusta tener esta línea a mano cuando exploro objetos desconocidos:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Vuelca el contenido de todo el objeto (junto con los cuerpos de los métodos) en mi registro de Firebug. Muy manejable.

Damir Zekić
fuente
El enlace ahora está roto. Seguro que me gustaría ver el punto de referencia, si alguien tiene otro enlace.
Billbad
Ya no está roto.
Olli
¿Los bucles Foreach se rompen en el prototipo? Como ahora es comúnmente soportado, eso es algo que el prototipo debería resolver.
mvrak
7

Aquí hay algo que hice.

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

así es como lo usaría
, funcionará en matrices y objetos (como una lista de elementos HTML)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Acabo de hacer esto, así que estoy abierto a sugerencias :)


fuente
Grandes cosas, ¡bastante sencillo!
Chris
6

Usaría los diferentes métodos según cómo quisiera hacer referencia a los elementos.

Use foreach si solo quiere el artículo actual.

Úselo si necesita un indexador para hacer comparaciones relativas. (Es decir, ¿cómo se compara esto con el elemento anterior / siguiente?)

Nunca he notado una diferencia de rendimiento. Esperaría hasta tener un problema de rendimiento antes de preocuparme por ello.

Matt Lacey
fuente
Vea la respuesta de Bnos a continuación: porque ... no está haciendo lo que espera aquí y si lo usa, es muy posible que se divierta todo. Para el registro, Prototype hace las cosas de la manera correcta .
marcus.greasly
4

Con for (var i en miMatriz) puede bucle sobre objetos también, que contendrá el nombre de la clave y se puede acceder a la propiedad a través de miMatriz [i] . Además, cualquier método que haya agregado al objeto también se incluirá en el bucle, es decir, si usa un marco externo como jQuery o prototype, o si agrega métodos para objetar prototipos directamente, en un punto apuntaré a Esos métodos.

pilsetnieks
fuente
4

¡Cuidado!

Si tiene varias etiquetas de script y está buscando información en los atributos de la etiqueta, por ejemplo, debe usar la propiedad .length con un bucle for porque no es una matriz simple sino un objeto HTMLCollection.

https://developer.mozilla.org/en/DOM/HTMLCollection

Si usa la declaración foreach para (var i en su Lista), devolverá protecciones y métodos de la colección HTMLC en la mayoría de los navegadores.

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

Incluso si getElementsByTagName devuelve una NodeList, la mayoría de los navegadores devuelven una colección HTMLC: https://developer.mozilla.org/en/DOM/document.getElementsByTagName

Baptx
fuente
3

For in loops on Arrays no es compatible con Prototype. Si cree que podría necesitar usar esa biblioteca en el futuro, tendría sentido atenerse a los bucles.

http://www.prototypejs.org/api/array

Chase Seibert
fuente
Olvídese de "puede que necesite usar esa biblioteca". Piense en cambio "su JS podría incluirse con cualquier otra cosa que use esa biblioteca", porque los problemas aún se presentan.
ijw
3

He visto problemas con el "para cada uno" usando objetos y prototipos y matrices

entiendo que el para cada uno es para propiedades de objetos y NO para matrices

Benjamin Lee
fuente
3

Si realmente quieres acelerar tu código, ¿qué hay de eso?

for( var i=0,j=null; j=array[i++]; foo(j) );

es algo así como tener la lógica while dentro de la instrucción for y es menos redundante. Firefox también tiene Array.forEach y Array.filter.

fabjoa
fuente
2
¿Por qué esto aceleraría tu código? No puedo ver por qué las declaraciones de este tipo lo acelerarían.
Rup
3

Un código más corto y mejor según jsperf es

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}
bormat
fuente
1

Use el Array (). ForEach loop para aprovechar el paralelismo

PazoozaTest Pazman
fuente
44
JavaScript en el navegador es un evento de bucle concurrente, por Array.prototype.forEachlo que no ejecutará varias llamadas a la devolución de llamada en paralelo.
Mike Samuel
1

para (;;) es para matrices : [20,55,33]

for..in es para objetos : {x: 20, y: 55: z: 33}

angelito
fuente
0

¡¡¡Ten cuidado!!! Estoy usando Chrome 22.0 en Mac OS y tengo problemas con cada sintaxis.

No sé si esto es un problema del navegador, un problema de JavaScript o algún error en el código, pero es MUY extraño. Fuera del objeto funciona perfectamente.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works
Paulo Check
fuente
0

Hay una diferencia importante entre ambos. El for-in itera sobre las propiedades de un objeto, por lo que cuando el caso es una matriz, no solo iterará sobre sus elementos, sino también sobre la función "eliminar" que tiene.

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

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

Podrías usar el for-in con un if(myArray.hasOwnProperty(i)). Aún así, al iterar sobre matrices, siempre prefiero evitar esto y simplemente usar la instrucción for (;;).

aquecopar
fuente
0

Aunque ambos son muy parecidos, hay una pequeña diferencia:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

en este caso el resultado es:

NORMA PARA LAZO:

ARRAY [0] = A

ARRAY [1] = B

ARRAY [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

mientras que en este caso la salida es:

FOR-IN LOOP:

ARRAY [1] = B

ARRAY [2] = C

ARRAY [10] = D

ARRAY [ABC] = 123

Yash Srivastava
fuente
0

La instrucción for in permite recorrer los nombres de todas las propiedades de un objeto. Desafortunadamente, también recorre todos los miembros que fueron heredados a través de la cadena de prototipos. Esto tiene el efecto secundario negativo de servir funciones de método cuando el interés está en los miembros de datos.

Fayaz
fuente