¿Por qué es una mala idea utilizar "for...in" para la iteración de matrices?

Resuelto lYriCAlsSH asked hace 15 años • 28 respuestas

Me dijeron que no lo use for...incon matrices en JavaScript. ¿Por qué no?

lYriCAlsSH avatar Feb 01 '09 16:02 lYriCAlsSH
Aceptado

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

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

También considere que las bibliotecas de JavaScript pueden hacer cosas como esta, que afectarán 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
*/
Expandir fragmento

Kenan Banks avatar Feb 01 '2009 10:02 Kenan Banks

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

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

Además, la especificación no garantiza el orden de iteració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 el orden numérico.

Por ejemplo, en JScript (IE <= 8), el orden de enumeración incluso en objetos Array se define tal 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, extiendes el Array.prototypeobjeto (como lo hacen algunas bibliotecas como 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/ simple y antiguo while.

Cuando quieras enumerar solo las propiedades propias de un objeto (las que no se heredan), puedes 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 desde 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
  }
}
Christian C. Salvadó avatar Nov 23 '2010 21:11 Christian C. Salvadó

Hay tres razones por las que no debería utilizar for..initerar sobre elementos de 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; yo mismo lo hice) o las cambia 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 repetirse también; Las propiedades heredadas se pueden excluir marcando hasOwnProperty(), pero eso no le ayudará con las propiedades establecidas en el objeto de matriz.

  • for..inno se garantiza que preserve el orden de los elementos

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

Christoph avatar Feb 01 '2009 14:02 Christoph

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, también se incluirá. Es decir

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

Esto escribirá:

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

Y como nunca puedes estar seguro de que no se agregará nada a la cadena del prototipo, simplemente usa un bucle for para enumerar la matriz:

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

Esto escribirá:

0 = foo
1 = bar
Pim Jager avatar Feb 01 '2009 10:02 Pim Jager