javascript - node - w3schools js foreach




Por que usar “for… in” com a iteração de array é uma má ideia? (17)

Como os elementos JavaScript são salvos como propriedades de objeto padrão, não é aconselhável fazer iteração por meio de matrizes JavaScript usando para ... loops in porque os elementos normais e todas as propriedades enumeráveis ​​serão listados.

Em https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections

Foi-me dito para não usar for...in matrizes em JavaScript. Por que não?


A declaração do for-in por si só não é uma "má prática", no entanto, pode ser mal utilizada , por exemplo, para iterar sobre matrizes ou objetos do tipo array.

O objetivo da declaração de entrada é enumerar as propriedades do objeto. Essa declaração vai subir na cadeia de protótipos, também enumerando propriedades herdadas , algo que às vezes não é desejado.

Além disso, a ordem de iteração não é garantida pela especificação, o que significa que se você deseja "iterar" um objeto de matriz, com essa instrução você não pode ter certeza de que as propriedades (índices de matriz) serão visitadas na ordem numérica.

Por exemplo, em JScript (IE <= 8), a ordem de enumeração, mesmo em objetos Array, é definida quando as propriedades foram criadas:

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
}

Além disso, falando sobre propriedades herdadas, se você, por exemplo, estender o objeto Array.prototype (como algumas bibliotecas como o MooTools), essas propriedades também serão enumeradas:

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

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

Como eu disse antes para iterar sobre matrizes ou objetos do tipo array, o melhor é usar um loop seqüencial , como um loop for / while antigo.

Quando você deseja enumerar apenas as propriedades de um objeto (aquelas que não são herdadas), use o método hasOwnProperty :

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

E algumas pessoas até recomendam chamar o método diretamente do Object.prototype para evitar problemas se alguém adicionar uma propriedade chamada hasOwnProperty ao nosso objeto:

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

A razão é que uma construção:

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

às vezes pode ser totalmente diferente do outro:

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

Considere também que as bibliotecas JavaScript podem fazer coisas assim, o que afetará qualquer array que você criar:

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


Além das razões dadas em outras respostas, você pode não querer usar a estrutura "for ... in" se você precisar fazer matemática com a variável counter porque o loop itera através dos nomes das propriedades do objeto e então a variável é uma string.

Por exemplo,

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

escreverá

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

enquanto que,

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

escreverá

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

Claro, isso pode ser facilmente superado incluindo

ii = parseInt(ii);

no loop, mas a primeira estrutura é mais direta.


Além do fato de que for ... in loops sobre todas as propriedades enumeráveis ​​(que não é o mesmo que "all array elements"!), Veja http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf , seção 12.6.4 (5ª edição) ou 13.7.5.15 (7ª edição):

A mecânica e ordem de enumerar as propriedades ... não é especificada ...

(Ênfase minha)

Isso significa que, se um navegador quisesse, ele poderia passar pelas propriedades na ordem em que elas foram inseridas. Ou em ordem numérica. Ou em ordem lexical (onde "30" vem antes de "4"! Tenha em mente que todas as chaves de objeto - e, portanto, todos os índices de array - são na verdade strings, então isso faz sentido). Poderia passar por eles por balde, se implementasse objetos como tabelas hash. Ou pegue tudo isso e adicione "para trás". Um navegador pode até mesmo iterar aleatoriamente e ser compatível com ECMA-262, desde que tenha visitado cada propriedade exatamente uma vez.

Na prática, a maioria dos navegadores prefere iterar aproximadamente na mesma ordem. Mas não há nada dizendo que eles precisam. Essa implementação é específica e poderia mudar a qualquer momento se outra forma fosse considerada muito mais eficiente.

De qualquer maneira, for ... não tem conotação de ordem. Se você se preocupa com a ordem, seja explícito sobre ela e use um loop regular for um índice.


Além dos outros problemas, a sintaxe "for..in" provavelmente é mais lenta, porque o índice é uma string, não um inteiro.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'

Existem três razões pelas quais você não deve usar for..in para iterar os elementos da matriz:

  • for..in fará um loop sobre todas as propriedades próprias e herdadas do objeto array que não são DontEnum ; isso significa que se alguém adicionar propriedades ao objeto de matriz específico (há razões válidas para isso - eu mesmo fiz isso) ou alterei o Array.prototype (que é considerado uma prática ruim no código que deve funcionar bem com outros scripts), essas propriedades também serão iteradas; propriedades herdadas podem ser excluídas verificando hasOwnProperty() , mas isso não o ajudará com propriedades definidas no próprio objeto de matriz

  • for..in não é garantido para preservar a ordem dos elementos

  • é lento porque você tem que percorrer todas as propriedades do objeto array e toda a sua cadeia de protótipos e ainda assim obterá apenas o nome da propriedade, ou seja, para obter o valor, uma pesquisa adicional será necessária


Isoladamente, não há nada de errado com o uso de in-in em arrays. For-in itera sobre os nomes das propriedades de um objeto e, no caso de um array "out-of-the-box", as propriedades correspondem aos índices da matriz. (Os propertes internos como length , toString e assim por diante não são incluídos na iteração.)

No entanto, se o seu código (ou a estrutura que você está usando) adicionar propriedades personalizadas a matrizes ou ao protótipo de matriz, essas propriedades serão incluídas na iteração, o que provavelmente não é o que você deseja.

Alguns frameworks JS, como o Prototype, modificam o protótipo do Array. Outras estruturas, como o JQuery, não, portanto, com o JQuery, você pode usar com segurança para entrar.

Se você está em dúvida, você provavelmente não deve usar para-in.

Uma maneira alternativa de iterar através de um array é usando um loop for:

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

No entanto, isso tem um problema diferente. A questão é que um array JavaScript pode ter "buracos". Se você definir arr como:

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

Em seguida, a matriz tem dois itens, mas um comprimento de 101. Usando for-in produzirá dois índices, enquanto o for-loop produzirá 101 índices, em que 99 terá um valor de undefined .


O for / in trabalha com dois tipos de variáveis: hashtables (matrizes associativas) e array (não-associativas).

O JavaScript determinará automaticamente a maneira como ele passa pelos itens. Então, se você sabe que seu array é realmente não-associativo, você pode usar for (var i=0; i<=arrayLen; i++) e ignorar a iteração de detecção automática.

Mas na minha opinião, é melhor usar for / in , o processo necessário para que a detecção automática seja muito pequena.

Uma resposta real para isso dependerá de como o navegador analisa / interpreta o código JavaScript. Pode mudar entre navegadores.

Não consigo pensar em outros propósitos para não 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]);

O problema com for ... in ... - e isso só se torna um problema quando um programador realmente não entende a linguagem; não é realmente um bug ou qualquer coisa - é que itera sobre todos os membros de um objeto (bem, todos os membros enumeráveis , mas isso é um detalhe por agora). Quando você deseja iterar apenas as propriedades indexadas de uma matriz, a única maneira garantida de manter as coisas semanticamente consistentes é usar um índice inteiro (ou seja, a for (var i = 0; i < array.length; ++i) loop de estilo).

Qualquer objeto pode ter propriedades arbitrárias associadas a ele. Não haveria nada de terrível em carregar propriedades adicionais em uma instância de matriz, em particular. O código que deseja ver apenas propriedades indexadas como matrizes, portanto, deve se ater a um índice inteiro. Código que está totalmente ciente do que for ... in e precisa realmente ver todas as propriedades, então tudo bem também.


Porque iterará sobre propriedades pertencentes a objetos na cadeia de protótipos se você não for cuidadoso.

Você pode usar for.. in , apenas certifique-se de verificar cada propriedade com hasOwnProperty .


Porque para ... enumera através do objeto que contém o array, não o próprio array. Se eu adicionar uma função à cadeia de protótipos de arrays, isso também será incluído. Ou seja

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

Isto irá escrever:

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

E como você nunca pode ter certeza de que nada será adicionado à cadeia de protótipos, apenas use um loop for para enumerar a matriz:

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

Isto irá escrever:

0 = foo
1 = bar

Resposta curta: Não vale a pena.

Resposta mais longa: Não vale a pena, mesmo que a ordem dos elementos sequenciais e o desempenho ideal não sejam necessários.

Resposta longa: Não vale a pena, pelas seguintes razões:

  • O uso for (var i in array) {} fará com que o 'array' seja interpretado como qualquer outro objeto puro , atravessando a cadeia de propriedades do objeto e, por fim, com desempenho mais lento que um loop baseado for índice.
  • Não é garantido retornar as propriedades do objeto em ordem seqüencial como se poderia esperar.
  • Usar as verificações hasOwnProperty() ou isNaN() para filtrar as propriedades do objeto é uma sobrecarga adicional, fazendo com que ele seja executado (ainda mais) mais lentamente. Além disso, introduzir tal lógica adicional nega a principal razão para usá-lo em primeiro lugar, ou seja, por causa do formato mais conciso.

Por essas razões, um compromisso aceitável entre desempenho e conveniência não existe. Realmente, não há nenhum benefício a menos que a intenção seja tratar a matriz como um objeto puro e executar operações nas propriedades do objeto da matriz.


Um aspecto importante é que for...in apenas itera sobre propriedades contidas em um objeto que tem seu atributo de propriedade enumerável definido como true. Portanto, se alguém tentar iterar sobre um objeto usando for...in então, propriedades arbitrárias podem ser perdidas se seu atributo de propriedade enumerável for falso. É bem possível alterar o atributo de propriedade enumerável para objetos Array normais para que determinados elementos não sejam enumerados. Embora, em geral, os atributos da propriedade tendem a se aplicar às propriedades da função dentro de um objeto.

Pode-se verificar o valor do atributo de propriedade enumerável de uma propriedade por:

myobject.propertyIsEnumerable('myproperty')

Ou para obter todos os quatro atributos de propriedade:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Este é um recurso disponível no ECMAScript 5 - em versões anteriores, não era possível alterar o valor do atributo de propriedade enumerável (sempre era definido como true).


TL & DR: Usar o loop for in em arrays não é mal, na verdade é exatamente o oposto.

Eu acho que o for in loop é uma jóia do JS se usado corretamente em matrizes. Espera-se que você tenha controle total sobre o seu software e saiba o que está fazendo. Vamos ver os inconvenientes mencionados e refutá-los um por um.

  1. Ele também passa por propriedades herdadas: Primeiro, todas as extensões para o Array.prototype devem ter sido feitas usando Object.defineProperty() e seu descritor enumerable deve ser definido como false . Qualquer biblioteca que não esteja fazendo isso não deve ser usada.
  2. As propriedades que você adiciona à cadeia de herança mais tarde são contadas: Ao fazer a subclassificação de array por Object.setPrototypeOf ou por Class extend . Você deve novamente usar Object.defineProperty() que, por padrão, define os descritores de propriedade writable , enumerable e configurable como false . Vamos ver um exemplo de subclassificação de array aqui ...

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]);

Então você vê .. for in loop está agora seguro desde que você se importava com o seu código.

  1. O loop for for in é lento: não. É de longe o método mais rápido de iteração se você estiver em loop sobre matrizes esparsas que são necessárias de tempos em tempos. Este é um dos truques de desempenho mais importantes que se deve saber. Vamos ver um exemplo. Vamos percorrer um array esparso.

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:");


para ... in é útil ao trabalhar em um objeto em JavaScript, mas não para um Array, mas ainda não podemos dizer que é um caminho errado, mas não é recomendado, veja este exemplo abaixo usando para ... 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, vamos fazer isso com o Array agora:

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

Como você vê o resultado é o mesmo ...

Mas vamos tentar algo, vamos prototipar algo para o Array ...

Array.prototype.someoneelse = "someoneelse";

Agora nós criamos um novo 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

Você vê o someelse !!! ... Na verdade, nós passamos pelo novo objeto Array neste caso!

Então essa é uma das razões pelas quais precisamos usar for..in com cuidado, mas nem sempre é o caso ...


Usar o for...inloop para uma matriz não está errado, embora eu possa imaginar por que alguém lhe disse isso:

1.) Já existe uma função de ordem superior, ou método, que tem esse propósito para uma matriz, mas tem mais funcionalidade e sintaxe mais enxuta, chamada 'forEach': Array.prototype.forEach(function(element, index, array) {} );

2.) Matrizes sempre ter um comprimento, mas for...ine forEachnão executar uma função para qualquer valor que é 'undefined', apenas para os índices que têm um valor definido. Portanto, se você atribuir apenas um valor, esses loops só executarão uma função uma vez, mas como uma matriz é enumerada, ela sempre terá um comprimento até o índice mais alto que tenha um valor definido, mas esse comprimento pode passar despercebido ao usá-los rotações.

3.) O padrão para loop executará uma função quantas vezes você definir nos parâmetros, e como uma matriz é numerada, faz mais sentido definir quantas vezes você deseja executar uma função. Ao contrário dos outros loops, o loop for pode executar uma função para cada índice na matriz, independentemente de o valor estar definido ou não.

Em essência, você pode usar qualquer loop, mas deve se lembrar exatamente como eles funcionam. Entenda as condições sob as quais os diferentes loops reiteram, suas funcionalidades separadas e perceba que eles serão mais ou menos apropriados para cenários diferentes.

Além disso, pode ser considerado uma prática melhor usar o forEachmétodo do que o for...inloop em geral, porque é mais fácil de escrever e tem mais funcionalidade, então você pode querer adquirir o hábito de usar somente este método e padrão, mas o seu ligar.

Veja abaixo que os dois primeiros loops executam apenas as instruções console.log uma vez, enquanto o padrão para loop executa a função quantas vezes forem especificadas, neste 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]




iteration