Pregunta ¿Por qué es una mala idea usar "para ... en" con iteración de matriz?


Me han dicho que no use for...in con matrices en JavaScript. Por qué no?


1539
2018-02-01 09:46


origen


Respuestas:


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 considere eso JavaScript las bibliotecas pueden hacer cosas como esta, que afectarán cualquier matriz que crees:

// 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
*/


1341
2018-02-01 10:08



los for-in declaración por sí misma no es una "mala práctica", sin embargo, puede ser mal usado, por ejemplo, a iterar sobre matrices u objetos tipo array.

El propósito de for-in declaración es para enumerar sobre las propiedades del objeto. Esta declaración irá en la cadena del prototipo, también enumerando heredado propiedades, una cosa que a veces no es deseado

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 instrucción no puede estar seguro de que las propiedades (índices de matriz) se visitarán en el orden numérico.

Por ejemplo, en JScript (IE <= 8), el orden de enumeración incluso en objetos Array se define a medida que 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 usted, por ejemplo, extiende la Array.prototype objeto (como algunas bibliotecas como MooTools), esas propiedades también serán enumeradas:

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

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

Como dije antes a iterar sobre matrices u objetos tipo array, lo mejor es usar un bucle secuencial, como un simple viejo for/while lazo.

Cuando desee enumerar solo el propiedades propias de un objeto (los que no son heredados), puede usar hasOwnProperty método:

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

Y algunas personas incluso recomiendan llamar al método directamente desde Object.prototype para evitar problemas si alguien agrega una propiedad llamada hasOwnProperty a nuestro objeto:

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

356
2017-11-23 21:22



Hay tres razones por las que no deberías usar for..in para iterar sobre los elementos de la matriz:

  • for..in recorrerá todas las propiedades propias y heredadas del objeto de matriz que no son DontEnum; eso significa que alguien agrega propiedades al objeto de matriz específico (hay razones válidas para esto - lo he hecho yo mismo) o cambiado Array.prototype (lo cual se considera una mala práctica en el código que se supone que funciona bien con otros scripts), estas propiedades se repetirán también; las propiedades heredadas se pueden excluir verificando hasOwnProperty(), pero eso no te ayudará con las propiedades establecidas en el objeto array

  • 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


101
2018-02-01 14:04



Porque para ... enumerar 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, 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 como nunca puede estar seguro de que no se agregará nada a la cadena del prototipo, simplemente use un ciclo 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

48
2018-02-01 10:08



De forma aislada, no hay nada de malo en utilizar 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. (A las propiedades incorporadas les gusta length, toString y así sucesivamente no están incluidos en la iteración).

Sin embargo, si su código (o la infraestructura 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 la que usted desea.

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

Si tiene dudas, probablemente no deba usar for-in.

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

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 defines arr como:

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

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


37
2018-02-01 10:34



Además de las razones dadas en otras respuestas, puede que no desee utilizar la estructura "para ... en" 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 cadena

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.


28
2017-09-02 02:29



A partir de 2016 (ES6) podemos usar for…of para iteración de matriz, como John Slegers ya notó.

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...of cuenta de 0 a 5, y también ignora Array.prototype.foo. Muestra una matriz valores.

  • for...in enumera solo el 5, ignorando índices de matriz indefinidos, pero agregando foo. Muestra una matriz nombres de propiedad.


24
2018-03-10 04:29



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

La mecánica y orden de enumerar las propiedades ... no está especificado...

(Énfasis mío)

Eso significa que si un navegador lo deseara, 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 de "4"! Tenga en cuenta que todas las teclas de objeto, y por lo tanto, todos los índices de matriz, son en realidad cadenas, por lo que tiene sentido). Podría ir a través de ellos por cubo, si implementa objetos como tablas hash. O tome algo de eso y agregue "al revés". Un navegador podría incluso iterar al azar y cumplir con ECMA-262, siempre que visite cada propiedad exactamente una vez.

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

De cualquier manera, for...in lleva consigo ninguna connotación de orden. Si le importa el orden, explíquelo y use un for bucle con un índice.


21
2018-05-14 16:26



Respuesta corta: simplemente no vale la pena.


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


Respuesta larga: simplemente no vale la pena, por los siguientes motivos:

  • Utilizando for (var i in array) {} causará que 'array' se interprete como cualquier otro puro objeto, atravesando la cadena de propiedad del objeto y, en última instancia, realizando un proceso más lento que un basado en índice for lazo.
  • No se garantiza que devuelva las propiedades del objeto en orden secuencial como podría esperarse.
  • Utilizando hasOwnProperty() o isNaN() las comprobaciones para filtrar las propiedades del objeto es una sobrecarga adicional que hace que funcione (incluso más) más lento. Además, la introducción de dicha lógica adicional niega la razón clave para usarla en primer lugar, es decir, debido al formato más conciso.

Por estas razones, no existe una compensación aceptable entre rendimiento y conveniencia. En realidad, no hay ningún beneficio a menos que la intención sea tratar la matriz como una puro objeto y realiza operaciones en las propiedades del objeto de la matriz.


20
2018-03-14 07:12