¿Por qué es una mala idea utilizar "for...in" para la iteración de matrices?
Me dijeron que no lo use for...in
con matrices en JavaScript. ¿Por qué no?
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 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
*/
La for-in
declaració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-in
declaració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.prototype
objeto (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 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 tener problemas si alguien agrega una propiedad nombrada hasOwnProperty
a nuestro objeto:
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// prop is not inherited
}
}
Hay tres razones por las que no debería utilizar for..in
iterar sobre elementos de matriz:
for..in
recorrerá todas las propiedades propias y heredadas del objeto de matriz que no lo seanDontEnum
; 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 cambiaArray.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 marcandohasOwnProperty()
, pero eso no le ayudará con las propiedades establecidas en el objeto de matriz.for..in
no se garantiza que preserve el orden de los elementoses 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
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