¿Por qué usar "for ... in" con iteración de matriz es una mala idea?

1824

Me han dicho que no lo use for...incon matrices en JavaScript. Por qué no?

lYriCAlsSH
fuente
45
Vi la pregunta reciente en la que alguien te dijo eso, pero solo significaban para matrices. Se considera una mala práctica para iterar a través de matrices, pero no necesariamente para iterar a través de miembros de un objeto.
mmurch
19
Muchas respuestas con bucles "for" como 'for (var i = 0; i <hColl.length; i ++) {}' compare con 'var i = hColl.length; mientras que (i--) {} 'que, cuando es posible usar la última forma, es sustancialmente más rápido. Sé que esto es tangencial, pero pensé que agregaría este bit.
Mark Schultheiss
2
@ MarkSchultheiss pero eso es iteración inversa. ¿Hay otra versión de iteración hacia adelante que sea más rápida?
ma11hew28
55
@Wynand use var i = hCol1.length; for (i;i;i--;) {}caché i ya que hará la diferencia y simplificará la prueba. - cuanto más antiguo es el navegador, más diferencia hay fory whileSIEMPRE almacena en caché el contador "i" - y, por supuesto, lo negativo no siempre se ajusta a la situación, y lo negativo es obfuscate un poco el código para algunas personas. y tenga en cuenta var i = 1000; for (i; i; i--) {}y var b =1000 for (b; b--;) {}donde voy de 1000 a 1 yb va de 999 a 0. - cuanto más antiguo es el navegador, más tiempo tiende a favorecer el rendimiento.
Mark Schultheiss
99
También puedes ser inteligente. for(var i = 0, l = myArray.length; i < l; ++i) ...es lo más rápido y mejor que puede obtener con la iteración hacia adelante.
Mathieu Amiot

Respuestas:

1557

La razón es que una construcción:

var a = []; // Create a new empty array.
a[5] = 5;   // Perfectly legal JavaScript that resizes the array.

for (var i = 0; i < a.length; i++) {
    // Iterate over numeric indexes from 0 to 5, as everyone expects.
    console.log(a[i]);
}

/* Will display:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

a veces puede ser totalmente diferente del otro:

var a = [];
a[5] = 5;
for (var x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
    console.log(x);
}

/* Will display:
   5
*/

También tenga en cuenta que las bibliotecas de JavaScript pueden hacer cosas como esta, lo que afectará cualquier matriz que cree:

// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'.
    console.log(x);
}

/* Will display:
   0
   1
   2
   3
   4
   foo
*/

Tríptico
fuente
146
¡Históricamente, algunos navegadores incluso iteraron sobre 'length', 'toString', etc.!
bobince 01 de
398
Recuerde usar en (var x in a)lugar de (x in a)- no quiere crear un global.
Chris Morgan
78
El primer problema no es una razón por la que es malo, solo una diferencia en la semántica. El segundo problema me parece una razón (además de los enfrentamientos entre bibliotecas que hacen lo mismo) de que alterar el prototipo de un tipo de datos incorporado es malo, en lugar de eso para ... en es malo.
Stewart
86
@ Stewart: Todos los objetos en JS son asociativos. Un JS Array es un objeto, así que sí, también es asociativo, pero no es para eso. Si desea iterar sobre las teclas de un objeto , use for (var key in object). Sin embargo, si desea iterar sobre los elementos de una matriz , úselos for(var i = 0; i < array.length; i += 1).
Martijn
42
Usted dijo para el primer ejemplo, que Itera sobre índices numéricos de 0 a 4, como todos esperan , ¡ espero que repita de 0 a 5 ! Dado que si agrega un elemento en la posición 5, la matriz tendrá 6 elementos (5 de ellos sin definir).
stivlo
393

La for-indeclaración en sí misma no es una "mala práctica", sin embargo, puede usarse mal , por ejemplo, para iterar sobre matrices u objetos similares a las matrices.

El propósito de la for-indeclaración es enumerar las propiedades de los objetos. Esta declaración subirá en la cadena del prototipo, enumerando también las propiedades heredadas , algo que a veces no se desea.

Además, el orden de iteración no está garantizado por la especificación, lo que significa que si desea "iterar" un objeto de matriz, con esta declaración no puede estar seguro de que las propiedades (índices de matriz) se visitarán en orden numérico.

Por ejemplo, en JScript (IE <= 8), el orden de enumeración incluso en los objetos Array se define como se crearon las propiedades:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

Además, hablando de propiedades heredadas, si, por ejemplo, extiende el Array.prototypeobjeto (como algunas bibliotecas como lo hacen MooTools), esas propiedades también se enumerarán:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

Como dije antes para iterar sobre matrices u objetos similares a matrices, lo mejor es usar un bucle secuencial , como un bucle for/ old simple while.

Cuando desee enumerar solo las propiedades propias de un objeto (las que no se heredan), puede usar el hasOwnPropertymétodo:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}

Y algunas personas incluso recomiendan llamar al método directamente Object.prototypepara evitar tener problemas si alguien agrega una propiedad nombrada hasOwnPropertya nuestro objeto:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}
CMS
fuente
10
Vea también la publicación de David Humphrey Iterating over Objects in JavaScript Quickly , porque la matriz for..ines mucho más lenta que los bucles "normales".
Chris Morgan
17
Pregunta sobre el último punto sobre "hasOwnProperty": si alguien anula "hasOwnProperty" en un objeto, tendrá problemas. ¿Pero no tendrá los mismos problemas si alguien anula "Object.prototype.hasOwnProperty"? De cualquier manera, te están jodiendo y no es tu responsabilidad, ¿verdad?
Scott Rippey
Dices for..inque no es una mala práctica, pero puede ser mal utilizada. ¿Tienes un ejemplo de buenas prácticas en el mundo real, en el que realmente querías revisar todas las propiedades de un objeto, incluidas las propiedades heredadas?
rjmunro
44
@ScottRippey: Si quieres llevarlo allí: youtube.com/watch?v=FrFUI591WhI
Nathan Wall
con esta respuesta encontré que puede acceder al valor confor (var p in array) { array[p]; }
equiman
117

Hay tres razones por las que no debe usar for..inpara iterar sobre elementos de la matriz:

  • for..inrecorrerá todas las propiedades propias y heredadas del objeto de matriz que no lo sean DontEnum; eso significa que si alguien agrega propiedades al objeto de matriz específico (hay razones válidas para esto, lo he hecho yo mismo) o ha cambiado Array.prototype(lo que se considera una mala práctica en el código que se supone que funciona bien con otros scripts), estas propiedades funcionarán ser iterado también; Las propiedades heredadas se pueden excluir marcando hasOwnProperty(), pero eso no lo ayudará con las propiedades establecidas en el objeto de matriz.

  • for..in no se garantiza que conserve el orden de los elementos

  • es lento porque tiene que recorrer todas las propiedades del objeto de matriz y toda su cadena de prototipos y solo obtendrá el nombre de la propiedad, es decir, para obtener el valor, se requerirá una búsqueda adicional

Christoph
fuente
55

Porque for ... in enumera a través del objeto que contiene la matriz, no la matriz en sí. Si agrego una función a la cadena de prototipos de matrices, eso también se incluirá. Es decir

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

Esto escribirá:

0 = foo
1 = barra
myOwnFunction = function () {alert (this); }

Y dado que nunca puede estar seguro de que no se agregará nada a la cadena de prototipos, solo use un bucle for para enumerar la matriz:

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

Esto escribirá:

0 = foo
1 = barra
Pim Jager
fuente
16
Las matrices son objetos, no existe un "objeto que contenga la matriz".
RobG
41

De forma aislada, no hay nada de malo en usar for-in en matrices. For-in itera sobre los nombres de propiedad de un objeto, y en el caso de una matriz "lista para usar", las propiedades corresponden a los índices de la matriz. (Las propiedades incorporadas length, como , toStringetc., no se incluyen en la iteración).

Sin embargo, si su código (o el marco que está utilizando) agrega propiedades personalizadas a las matrices o al prototipo de la matriz, estas propiedades se incluirán en la iteración, que probablemente no sea lo que desea.

Algunos marcos JS, como Prototype, modifican el prototipo Array. Otros marcos como JQuery no lo hacen, por lo que con JQuery puedes usar for-in de forma segura.

Si tiene dudas, probablemente no debería usar for-in.

Una forma alternativa de iterar a través de una matriz es usar un bucle for:

for (var ix=0;ix<arr.length;ix++) alert(ix);

Sin embargo, esto tiene un problema diferente. El problema es que una matriz de JavaScript puede tener "agujeros". Si define arrcomo:

var arr = ["hello"];
arr[100] = "goodbye";

Luego, la matriz tiene dos elementos, pero una longitud de 101. El uso de for-in producirá dos índices, mientras que el for-loop producirá 101 índices, donde el 99 tiene un valor de undefined.

JacquesB
fuente
37

A partir de 2016 (ES6) podemos usar for…ofpara la iteración de matrices, como John Slegers ya notó.

Solo me gustaría agregar este código de demostración simple, para aclarar las cosas:

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
    }

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
    }

La consola muestra:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

En otras palabras:

  • for...ofcuenta de 0 a 5 y también ignora Array.prototype.foo. Muestra los valores de la matriz .

  • for...inenumera solo el 5, ignorando los índices de matriz indefinidos, pero agregando foo. Muestra los nombres de propiedades de la matriz .

MarcG
fuente
32

Respuesta corta: simplemente no vale la pena.


Respuesta más larga: simplemente no vale la pena, incluso si no se requiere un orden secuencial de elementos y un rendimiento óptimo.


Respuesta larga: simplemente no vale la pena ...

  • El uso for (var property in array)hará arrayque se repita como un objeto , atravesando la cadena del prototipo del objeto y, en última instancia, funcionando más lentamente que un forbucle basado en índice .
  • for (... in ...) no se garantiza que devuelva las propiedades del objeto en orden secuencial, como cabría esperar.
  • El uso hasOwnProperty()y las !isNaN()comprobaciones para filtrar las propiedades del objeto es una sobrecarga adicional que hace que funcione aún más lentamente y niega la razón clave para usarlo en primer lugar, es decir, debido al formato más conciso.

Por estas razones, ni siquiera existe una compensación aceptable entre rendimiento y conveniencia. Realmente no hay ningún beneficio a menos que la intención sea manejar la matriz como un objeto y realizar operaciones en las propiedades del objeto de la matriz.

WynandB
fuente
31

Además de las razones dadas en otras respuestas, es posible que no desee usar la estructura "for ... in" si necesita hacer cálculos matemáticos con la variable de contador porque el ciclo itera a través de los nombres de las propiedades del objeto y, por lo tanto, la variable es una cuerda

Por ejemplo,

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

escribirá

0, number, 1
1, number, 2
...

mientras,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

escribirá

0, string, 01
1, string, 11
...

Por supuesto, esto se puede superar fácilmente incluyendo

ii = parseInt(ii);

en el bucle, pero la primera estructura es más directa.

ctmiddle
fuente
66
Puede usar el prefijo en +lugar de a parseIntmenos que realmente necesite un número entero o ignore caracteres no válidos.
Konrad Borowski
Además, parseInt()no se recomienda su uso. Pruebe parseInt("025");y se le fallará.
Derek 朕 會 功夫
66
@Derek 朕 會 功夫: definitivamente puedes usarlo parseInt. El problema es que si no incluye la raíz, los navegadores más antiguos podrían intentar interpretar el número (por lo tanto, 025 se convierte en octal). Esto se solucionó en ECMAScript 5, pero aún sucede para los números que comienzan con "0x" (interpreta el número como hexadecimal). Para estar seguro, use la raíz para especificar el número de esta manera parseInt("025", 10), que especifica la base 10.
IAmTimCorey
23

Aparte del hecho de que for... inrecorre todas las propiedades enumerables (que no es lo mismo que "todos los elementos de la matriz"), consulte http://www.ecma-international.org/publications/files/ECMA-ST/Ecma -262.pdf , sección 12.6.4 (5a edición) o 13.7.5.15 (7a edición):

La mecánica y el orden de enumerar las propiedades ... no se especifica ...

(El énfasis es mío).

Eso significa que si un navegador quisiera, podría pasar por las propiedades en el orden en que se insertaron. O en orden numérico. O en orden léxico (¡donde "30" viene antes que "4"! Tenga en cuenta que todas las claves de objeto, y por lo tanto, todos los índices de matriz, en realidad son cadenas, por lo que tiene mucho sentido). Podría atravesarlos por cubo si implementara objetos como tablas hash. O tome algo de eso y agregue "al revés". Un navegador podría incluso iterar al azar y ser compatible con ECMA-262, siempre que visite cada propiedad exactamente una vez.

En la práctica, a la mayoría de los navegadores les gusta iterar aproximadamente en el mismo orden. Pero no hay nada que diga que tienen que hacerlo. Esa es una implementación específica, y podría cambiar en cualquier momento si se descubriera que otra forma es mucho más eficiente.

De cualquier manera, for... no inconlleva connotación de orden. Si le importa el orden, sea explícito y use un forciclo regular con un índice.

cHao
fuente
18

Principalmente dos razones:

Uno

Como otros han dicho, es posible que obtenga claves que no están en su matriz o que se heredan del prototipo. Entonces, si, digamos, una biblioteca agrega una propiedad a los prototipos Array u Object:

Array.prototype.someProperty = true

Lo obtendrá como parte de cada matriz:

for(var item in [1,2,3]){
  console.log(item) // will log 1,2,3 but also "someProperty"
}

podrías resolver esto con el método hasOwnProperty:

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

pero esto es cierto para iterar sobre cualquier objeto con un ciclo for-in.

Dos

Por lo general, el orden de los elementos en una matriz es importante, pero el bucle for-in no necesariamente iterará en el orden correcto, eso es porque trata la matriz como un objeto, que es la forma en que se implementa en JS, y no como una matriz Esto parece algo pequeño, pero realmente puede arruinar las aplicaciones y es difícil de depurar.

Lior
fuente
2
Object.keys(a).forEach( function(item) { console.log(item) } )iterar sobre una matriz de claves de propiedad propias, no las heredadas del prototipo.
Qwerty
2
Es cierto, pero al igual que el ciclo for-in, no necesariamente estará en el orden de índice correcto. Además, no funcionará en navegadores antiguos que no admitan ES5.
Lior
Puede enseñar a esos navegadores array.forEachinsertando cierto código en sus scripts. Ver Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Qwerty
Por supuesto, pero estás manipulando el prototipo, y eso no siempre es una buena idea ... y aún así, tienes el problema del pedido ...
Lior
Y, por supuesto, la razón número tres: matrices dispersas.
un mejor oliver
16

Porque enumera a través de campos de objeto, no índices. Puede obtener valor con el índice "longitud" y dudo que quiera esto.

vava
fuente
Entonces, ¿cuál es la mejor manera de hacerlo?
lYriCAlsSH
3
for (var i = 0; i <arr.length; i ++) {}
vava
3
En firefox 3 también puede usar arr.forEach o for (var [i, v] en Iterator (arr)) {} pero ninguno de esos funciona en IE, aunque puede escribir para cada método usted mismo.
vava
y prácticamente todas las bibliotecas tienen su propio método para esto también.
vava
55
Esta respuesta es incorrecta. "longitud" no se incluirá en la iteración for-in. Solo se incluyen las propiedades que agrega usted mismo.
JacquesB 01 de
16

No creo que tenga mucho que agregar, por ejemplo. La respuesta del tríptico o la respuesta del CMS sobre por qué se for...indebe evitar el uso en algunos casos.

Sin embargo, me gustaría agregar que en los navegadores modernos hay una alternativa for...inque se puede usar en aquellos casos en los for...inque no se puede usar. Esa alternativa es for...of:

for (var item of items) {
    console.log(item);
}

Nota :

Desafortunadamente, ninguna versión de Internet Explorer es compatible for...of( Edge 12+ lo hace), por lo que tendrá que esperar un poco más hasta que pueda usarla en el código de producción del lado del cliente. Sin embargo, debería ser seguro usarlo en el código JS del lado del servidor (si usa Node.js ).

John Slegers
fuente
@georgeawg Querías decir for-of, no for-in, ¿verdad?
ᆼ ᆺ ᆼ
15

El problema con for ... in ...- y esto solo se convierte en un problema cuando un programador realmente no entiende el lenguaje; en realidad no es un error ni nada, es que itera sobre todos los miembros de un objeto (bueno, todos los miembros enumerables , pero eso es un detalle por ahora). Cuando desee iterar solo sobre las propiedades indexadas de una matriz, la única forma garantizada de mantener las cosas semánticamente consistentes es usar un índice entero (es decir, un for (var i = 0; i < array.length; ++i)bucle de estilo).

Cualquier objeto puede tener propiedades arbitrarias asociadas a él. No habría nada terrible en cargar propiedades adicionales en una instancia de matriz, en particular. El código que quiere ver solo propiedades indexadas de tipo matriz, por lo tanto, debe adherirse a un índice entero. Código que es plenamente consciente de lo for ... inque realmente necesita ver todas las propiedades, bueno, eso también está bien.

Puntiagudo
fuente
Buena explicación puntiaguda. Sólo curioso. Si tuviera una matriz que estaba dentro de un objeto bajo propiedades de multiplicación y for in, en comparación con un ciclo for regular, ¿esas matrices se iterarían? (lo que en esencia sería un rendimiento lento, ¿verdad?)
NiCk Newman
2
@NiCkNewman bien, el objeto al que hace referencia inen un for ... inbucle simplemente
Pointy
Veo. Simplemente curioso porque tengo objetos y matrices dentro de mi objeto principal del juego y me preguntaba si para la entrada sería más doloroso que simplemente un ciclo for regular en los índices.
NiCk Newman
@NiCkNewman, bueno, el tema de toda esta pregunta es que simplemente no debes usarlo for ... inen matrices; Hay muchas buenas razones para no hacerlo. No es tanto un problema de rendimiento como un problema de "asegúrese de que no se rompa".
Puntiagudo
Bueno, mis objetos se almacenan técnicamente en una matriz, por eso estaba preocupado, algo así como: [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]pero sí, lo entiendo.
NiCk Newman
9

Además, debido a la semántica, la forma en que for, intrata las matrices (es decir, lo mismo que cualquier otro objeto JavaScript) no está alineada con otros lenguajes populares.

// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"

// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x);          //Output: "ABC"

// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x;                    //Output: "ABC"

// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x);            //Output: "012"
matpop
fuente
9

TL&DR: Usar el for inbucle en matrices no es malo, de hecho, todo lo contrario.

Creo que el for inbucle es una joya de JS si se usa correctamente en matrices. Se espera que tenga control total sobre su software y sepa lo que está haciendo. Veamos los inconvenientes mencionados y refutemos uno por uno.

  1. También recorre las propiedades heredadas: en primer lugar, cualquier extensión de la Array.prototypedebe haberse realizado mediante el uso Object.defineProperty()y su enumerabledescriptor debe establecerse en false. Cualquier biblioteca que no lo haga no debe usarse en absoluto.
  2. Las propiedades que agrega a la cadena de herencia más tarde se cuentan: al hacer una subclasificación de matrices por Object.setPrototypeOfo por clase extend. Debe utilizar una vez más Object.defineProperty(), que por defecto los conjuntos writable, enumerabley los configurabledescriptores de propiedades a false. Veamos un ejemplo de subclasificación de matriz aquí ...

function Stack(...a){
  var stack = new Array(...a);
  Object.setPrototypeOf(stack, Stack.prototype);
  return stack;
}
Stack.prototype = Object.create(Array.prototype);                                 // now stack has full access to array methods.
Object.defineProperty(Stack.prototype,"constructor",{value:Stack});               // now Stack is a proper constructor
Object.defineProperty(Stack.prototype,"peak",{value: function(){                  // add Stack "only" methods to the Stack.prototype.
                                                       return this[this.length-1];
                                                     }
                                             });
var s = new Stack(1,2,3,4,1);
console.log(s.peak());
s[s.length] = 7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);

for(var i in s) console.log(s[i]);

Así que ya ves ... el for inbucle ahora es seguro ya que te importa tu código.

  1. El for inciclo es lento: infierno no. Es, con mucho, el método de iteración más rápido si realiza un bucle sobre matrices dispersas que se necesitan de vez en cuando. Este es uno de los trucos de rendimiento más importantes que uno debe saber. Veamos un ejemplo. Vamos a recorrer un conjunto disperso.

var a = [];
a[0] = "zero";
a[10000000] = "ten million";
console.time("for loop on array a:");
for(var i=0; i < a.length; i++) a[i] && console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");
for(var i in a) a[i] && console.log(a[i]);
console.timeEnd("for in loop on array a:");

Redu
fuente
@ Ravi Shanker Reddy Buena configuración de benchmarking. Como he mencionado en mi respuesta, el for inbucle eclipsa a los demás "si" la matriz es escasa y más si se hace más grande. Así que he reorganizado la prueba de banco para una matriz dispersa arr, de tamaño ~ 10000 con solo 50 elementos elegidos [42,"test",{t:1},null, void 0]al azar entre índices aleatorios. Notará inmediatamente la diferencia. - >> Compruébalo aquí << - .
Reducir el
8

Además de los otros problemas, la sintaxis "for..in" es probablemente más lenta, porque el índice es una cadena, no un entero.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'
dc1
fuente
Probablemente no importa mucho. Los elementos de matriz son propiedades de un objeto basado en matriz o similar a una matriz, y todas las propiedades de objeto tienen claves de cadena. A menos que su motor JS lo optimice de alguna manera, incluso si usara un número, terminaría convirtiéndose en una cadena para la búsqueda.
cHao
Independientemente de cualquier problema de rendimiento, si es nuevo en JavaScript, use var i in ay espere que el índice sea un número entero, entonces hacer algo como a[i+offset] = <value>colocar los valores en lugares completamente incorrectos. ("1" + 1 == "11").
szmoore
8

Un aspecto importante es que for...insolo itera sobre las propiedades contenidas en un objeto que tienen su atributo de propiedad enumerable establecido en verdadero. Entonces, si uno intenta iterar sobre un objeto usando, entonces se pueden perder propiedades arbitrarias si su atributo de propiedad enumerable es falso. Es muy posible alterar el atributo de propiedad enumerable para los objetos Array normales para que ciertos elementos no se enumeren. Aunque, en general, los atributos de propiedad tienden a aplicarse a las propiedades de función dentro de un objeto.for...in

Se puede verificar el valor del atributo de propiedad enumerable de una propiedad de la siguiente manera:

myobject.propertyIsEnumerable('myproperty')

O para obtener los cuatro atributos de propiedad:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Esta es una característica disponible en ECMAScript 5: en versiones anteriores no era posible alterar el valor del atributo de propiedad enumerable (siempre se estableció en verdadero).

Pierz
fuente
8

El for/in funciona con dos tipos de variables: tablas hash (matrices asociativas) y matrices (no asociativas).

JavaScript determinará automáticamente la forma en que pasa a través de los elementos. Entonces, si sabe que su matriz es realmente no asociativa, puede usarfor (var i=0; i<=arrayLen; i++) y omitir la iteración de detección automática.

Pero en mi opinión, es mejor usar for/in , el proceso requerido para esa autodetección es muy pequeño.

Una respuesta real para esto dependerá de cómo el navegador analice / interprete el código JavaScript. Puede cambiar entre navegadores.

No puedo pensar en otros propósitos para no usar for/ in;

//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
   alert(arr[i]);

//Associative
var arr = {
   item1 : 'a',
   item2 : 'b',
   item3 : 'c'
};

for (var i in arr)
   alert(arr[i]);
Ricardo
fuente
Es cierto, a menos que esté utilizando objetos prototipados. ;) abajo
Ricardo
Eso Arrayes porque Objecttambién lo es
Consultoría gratuita
2
for ... intrabaja con objetos No existe la autodetección.
un mejor oliver
7

Porque no iterará sobre las propiedades que pertenecen a los objetos en la cadena del prototipo si no tiene cuidado.

Puede usar for.. in, solo asegúrese de verificar cada propiedad con hasOwnProperty .

JAL
fuente
2
No es suficiente: está perfectamente bien agregar propiedades con nombre arbitrarias a las instancias de matriz, y esas se probarán a truepartir de las hasOwnProperty()verificaciones.
Puntiagudo
Buen punto, gracias. Nunca he sido tan tonto como para hacerle eso a un Array, ¡así que no lo he considerado!
JAL
1
@Pointy No he probado esto, pero quizás esto se pueda superar usando un isNaNcheque en cada nombre de propiedad.
WynandB
1
@Wynand idea interesante; sin embargo, realmente no veo por qué vale la pena cuando iterar con un índice numérico simple es tan fácil.
Pointy
@WynandB perdón por el golpe, pero siento que una corrección está en orden: isNaNes para verificar si una variable es el valor especial NaN o no, no se puede usar para verificar 'cosas que no sean números' (puede ir con un regular typeof para eso).
doldt
6

No es necesariamente malo (en función de lo que está haciendo), pero en el caso de las matrices, si se ha agregado algo Array.prototype, obtendrá resultados extraños. Donde esperarías que este ciclo se ejecute tres veces:

var arr = ['a','b','c'];
for (var key in arr) { ... }

Si una función llamada helpfulUtilityMethodha sido añadido a Array's prototype, entonces el bucle terminaría corriendo cuatro veces: keysería 0, 1, 2, y helpfulUtilityMethod. Si solo esperabas números enteros, ¡ay!

josh3736
fuente
6

Debe usar el for(var x in y)único en las listas de propiedades, no en los objetos (como se explicó anteriormente).

usuario268396
fuente
13
Solo una nota sobre SO: no hay 'arriba' porque los comentarios cambian el orden en la página todo el tiempo. Entonces, realmente no sabemos a qué comentario te refieres. Es bueno decir "en el comentario de x persona" por este motivo.
JAL
@JAL ... o agregue el enlace permanente a la respuesta.
WynandB
5

Usar el for...inbucle para una matriz no está mal, aunque puedo adivinar por qué alguien te dijo eso:

1.) Ya existe una función o método de orden superior que tiene ese propósito para una matriz, pero tiene más funcionalidad y una sintaxis más simple, llamada 'forEach': Array.prototype.forEach(function(element, index, array) {} );

2.) Las matrices siempre tienen una longitud, pero for...iny forEachno ejecutan una función para cualquier valor que es'undefined' , sólo para los índices que tienen un valor definido. Entonces, si solo asigna un valor, estos bucles solo ejecutarán una función una vez, pero dado que se enumera una matriz, siempre tendrá una longitud hasta el índice más alto que tenga un valor definido, pero esa longitud podría pasar desapercibida al usar estos bucles

3.) El estándar for loop ejecutará una función tantas veces como usted defina en los parámetros, y dado que una matriz está numerada, tiene más sentido definir cuántas veces desea ejecutar una función. A diferencia de los otros bucles, el bucle for puede ejecutar una función para cada índice de la matriz, ya sea que el valor esté definido o no.

En esencia, puede usar cualquier bucle, pero debe recordar exactamente cómo funcionan. Comprenda las condiciones bajo las cuales los diferentes bucles reiteran, sus funcionalidades separadas, y tenga en cuenta que serán más o menos apropiados para diferentes escenarios.

Además, puede considerarse una mejor práctica usar el forEachmétodo que el for...inbucle en general, porque es más fácil de escribir y tiene más funcionalidad, por lo que es posible que desee acostumbrarse a usar solo este método y estándar para, pero su llamada.

Vea a continuación que los dos primeros bucles solo ejecutan las instrucciones console.log una vez, mientras que el bucle estándar para ejecuta la función tantas veces como se especifique, en este caso, array.length = 6.

var arr = [];
arr[5] = 'F';

for (var index in arr) {
console.log(index);
console.log(arr[index]);
console.log(arr)
}
// 5
// 'F'
// => (6) [undefined x 5, 6]

arr.forEach(function(element, index, arr) {
console.log(index);
console.log(element);
console.log(arr);
});
// 5
// 'F'
// => Array (6) [undefined x 5, 6]

for (var index = 0; index < arr.length; index++) {
console.log(index);
console.log(arr[index]);
console.log(arr);
};
// 0
// undefined
// => Array (6) [undefined x 5, 6]

// 1
// undefined
// => Array (6) [undefined x 5, 6]

// 2
// undefined
// => Array (6) [undefined x 5, 6]

// 3
// undefined
// => Array (6) [undefined x 5, 6]

// 4
// undefined
// => Array (6) [undefined x 5, 6]

// 5
// 'F'
// => Array (6) [undefined x 5, 6]
mrmaclean89
fuente
4

Estas son las razones por las cuales esta (generalmente) es una mala práctica:

  1. for...inlos bucles iteran sobre todas sus propiedades enumerables y las propiedades enumerables de sus prototipos. Por lo general, en una iteración de matriz solo queremos iterar sobre la matriz misma. Y aunque usted mismo no pueda agregar nada a la matriz, sus bibliotecas o marco pueden agregar algo.

Ejemplo :

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...inlos bucles no garantizan un orden de iteración específico . Aunque este orden generalmente se ve en la mayoría de los navegadores modernos en estos días, todavía no hay una garantía del 100%.
  2. for...inlos bucles ignoran los undefinedelementos de la matriz, es decir, los elementos de la matriz que aún no se han asignado.

Ejemplo :

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}

Willem van der Veen
fuente
2

for ... in es útil cuando se trabaja en un objeto en JavaScript, pero no para un Array, pero aún así no podemos decir que sea una forma incorrecta, pero no se recomienda, mira este ejemplo a continuación usando for ... in loop:

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

OK, hagámoslo con Array ahora:

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

Como veis el resultado igual ...

Pero intentemos algo, prototipémonos algo para Array ...

Array.prototype.someoneelse = "someoneelse";

Ahora creamos un nuevo Array ();

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

Ves a alguien ! ... En realidad, estamos recorriendo un nuevo objeto Array en este caso.

Así que esa es una de las razones por las que debemos usar ... con cuidado, pero no siempre es así ...

Alireza
fuente
2

A for ... in loop siempre enumera las claves. Las claves de propiedades de los objetos son siempre cadenas, incluso las propiedades indexadas de una matriz:

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123
Maher Tliba
fuente
0

aunque no se aborda específicamente en esta pregunta, agregaría que hay una muy buena razón para no usar para ... en un NodeList(como se obtendría de unquerySelectorAll llamada, ya que no ve los elementos devueltos, en su lugar iterando solo sobre las propiedades NodeList.

en el caso de un solo resultado, obtuve:

var nodes = document.querySelectorAll(selector);
nodes
 NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

lo que explicaba por qué mi for (node in nodes) node.href = newLink;fallaba.

jcomeau_ictx
fuente