Por que usar "for ... in" com iteração de matriz é uma má idéia?

1823

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

lYriCAlsSH
fonte
45
Vi a pergunta recente em que alguém disse isso para você, mas eles apenas significaram para matrizes. É considerado uma má prática para iterar através de matrizes, mas não necessariamente para iterar através de membros de um objeto.
mmurch
19
Muitas respostas com loops "for" como 'for (var i = 0; i <hColl.length; i ++) {}' são comparadas com 'var i = hColl.length; while (i--) {} 'que, quando é possível usar a última forma, é substancialmente mais rápido. Eu sei que isso é tangencial, mas pensei em adicionar este pouco.
Mark Schultheiss
2
@ MarkSchultheiss, mas isso é iteração reversa. Existe outra versão da iteração direta mais rápida?
ma11hew28
5
O @Wynand usa o var i = hCol1.length; for (i;i;i--;) {}cache i, pois fará a diferença e simplifica o teste. - quanto mais antigo o navegador, maior a diferença entre fore whileSEMPRE armazena em cache o contador "i" - e, claro, o negativo nem sempre se encaixa na situação, e o negativo, embora obfuscate o código seja um pouco para algumas pessoas. e observe var i = 1000; for (i; i; i--) {}e var b =1000 for (b; b--;) {}onde eu vou de 1000 para 1 eb vai de 999 a 0. - quanto mais antigo o navegador, mais o tempo tende a favorecer o desempenho.
Mark Schultheiss
9
Você também pode ser inteligente. for(var i = 0, l = myArray.length; i < l; ++i) ...é o mais rápido e melhor que você pode obter com a iteração direta.
Mathieu Amiot

Respostas:

1557

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

Tríptico
fonte
146
Historicamente, alguns navegadores até iteravam sobre 'comprimento', 'toString' etc.!
9990
398
Lembre-se de usar em (var x in a)vez de (x in a)- não queira criar um global.
Chris Morgan
78
A primeira questão não é um motivo para ser ruim, apenas uma diferença na semântica. A segunda questão parece-me uma razão (além de conflitos entre bibliotecas que fazem o mesmo) que alterar o protótipo de um tipo de dados interno é ruim, e não para .. é ruim.
Stewart
86
@ Stewart: Todos os objetos em JS são associativos. Uma matriz JS é um objeto; portanto, também é associativo, mas não é para isso que serve. Se você deseja percorrer as chaves de um objeto , use for (var key in object). Se você deseja iterar sobre os elementos de uma matriz , use for(var i = 0; i < array.length; i += 1).
Martijn
42
Você disse para o primeiro exemplo, que itera sobre índices numéricos de 0 a 4, como todos esperam , espero que itereça de 0 a 5 ! Como se você adicionar um elemento na posição 5, a matriz terá 6 elementos (5 deles indefinidos).
stivlo
393

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

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

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

Por exemplo, em JScript (IE <= 8), a ordem de enumeração mesmo nos objetos Array é definida conforme 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 Array.prototypeobjeto (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 semelhantes a matrizes, a melhor coisa é usar um loop seqüencial , como um loop simples for/ antigo while.

Quando você deseja enumerar apenas as próprias propriedades de um objeto (aquelas que não são herdadas), você pode usar o hasOwnPropertymétodo:

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

E algumas pessoas até recomendam chamar o método diretamente Object.prototypepara evitar problemas se alguém adicionar uma propriedade nomeada hasOwnPropertyao nosso objeto:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}
CMS
fonte
10
Veja também o post de David Humphrey Iterando rapidamente objetos em JavaScript - pois os arrays for..insão muito mais lentos que os loops "normais".
Chris Morgan
17
Pergunta sobre o último ponto sobre "hasOwnProperty": se alguém substituir "hasOwnProperty" em um objeto, você terá problemas. Mas você não terá os mesmos problemas se alguém substituir "Object.prototype.hasOwnProperty"? De qualquer maneira, eles estão estragando tudo e não é sua responsabilidade, certo?
Scott Rippey
Você diz que for..innão é uma prática ruim, mas pode ser mal utilizada. Você tem um exemplo do mundo real de boas práticas, onde realmente deseja examinar todas as propriedades de um objeto, incluindo propriedades herdadas?
Rjmunro 03/07/2013
4
@ScottRippey: Se você quiser levá-lo para lá: youtube.com/watch?v=FrFUI591WhI
Nathan Wall
com essa resposta eu achei que pode acessar o valor comfor (var p in array) { array[p]; }
equiman
117

Há três razões pelas quais você não deve usar for..inpara iterar sobre os elementos da matriz:

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

  • for..in não é garantido para preservar a ordenação de elementos

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

Christoph
fonte
55

Porque for ... in enumera através do objeto que contém a matriz, não a própria matriz. Se eu adicionar uma função à cadeia de protótipos de matrizes, ela também será incluída. 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, 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
Pim Jager
fonte
16
Matrizes são objetos, não há "objeto que retenha a matriz".
RobG
41

Isoladamente, não há nada errado em usar a entrada nas matrizes. For-in itera sobre os nomes de propriedades de um objeto e, no caso de uma matriz "pronta para uso", as propriedades correspondem aos índices da matriz. (As propriedades internas como length, toStringe assim por diante, não estão incluídas na iteração.)

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

Algumas estruturas JS, como Prototype, modificam o protótipo Array. Outras estruturas, como o JQuery, não, portanto, com o JQuery, você pode usar o for-in com segurança.

Se você estiver em dúvida, provavelmente não deve usar o for-in.

Uma maneira alternativa de iterar através de uma matriz é usar um loop for:

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

No entanto, isso tem um problema diferente. O problema é que uma matriz JavaScript pode ter "buracos". Se você definir arrcomo:

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

Em seguida, a matriz possui dois itens, mas um comprimento de 101. Usar for-in produzirá dois índices, enquanto o loop for produzirá 101 índices, nos quais o 99 tem um valor de undefined.

JacquesB
fonte
37

A partir de 2016 (ES6), poderemos usar for…ofpara iteração de matriz, como John Slegers já notou.

Gostaria apenas de adicionar este código de demonstração simples, para tornar as coisas mais claras:

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++;
    }

O console mostra:

for...of:

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

for...in:

0: 5
1: foo

Em outras palavras:

  • for...ofconta de 0 a 5 e também ignora Array.prototype.foo. Mostra valores de matriz .

  • for...inlista apenas os 5, ignorando índices de matriz indefinidos, mas adicionando foo. Ele mostra os nomes das propriedades da matriz .

MarcG
fonte
32

Resposta curta: simplesmente não vale a pena.


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


Resposta longa: não vale a pena ...

  • O uso for (var property in array)fará com arrayque seja iterado como um objeto , atravessando a cadeia de protótipos de objetos e, finalmente, executando mais lentamente que um forloop baseado em índice .
  • for (... in ...) não é garantido o retorno das propriedades do objeto em ordem seqüencial, como seria de esperar.
  • O uso hasOwnProperty()e as !isNaN()verificações para filtrar as propriedades do objeto são uma sobrecarga adicional, o que resulta em um desempenho ainda mais lento e nega o principal motivo para usá-lo em primeiro lugar, ou seja, devido ao formato mais conciso.

Por esses motivos, nem sequer existe uma compensação aceitável entre desempenho e conveniência. Realmente não há benefício, a menos que a intenção seja manipular a matriz como um objeto e executar operações nas propriedades do objeto da matriz.

WynandB
fonte
31

Além dos motivos apresentados em outras respostas, talvez você não queira usar a estrutura "for ... in" se precisar fazer contas com a variável counter, porque o loop itera os nomes das propriedades do objeto e, portanto, 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
...

Obviamente, isso pode ser facilmente superado, incluindo

ii = parseInt(ii);

no loop, mas a primeira estrutura é mais direta.

ctmiddle
fonte
6
Você pode usar o prefixo em +vez de, a parseIntmenos que realmente precise de um número inteiro ou ignore caracteres inválidos.
22912 Konrad Borowski
Além disso, o uso parseInt()não é recomendado. Experimente parseInt("025");e vai falhar.
Derek朕會功夫
6
@Derek 功夫 會 功夫 - você pode definitivamente usar parseInt. O problema é que, se você não incluir o radical, navegadores mais antigos podem tentar interpretar o número (assim, 025 se torna octal). Isso foi corrigido no ECMAScript 5, mas ainda acontece com números começando com "0x" (ele interpreta o número como hexadecimal). Para estar no lado seguro, use a raiz para especificar o número como assim parseInt("025", 10)- que especifica base 10.
IAmTimCorey
23

Além do fato de que for... incircula todas as propriedades enumeráveis ​​(que não são iguais a "todos os elementos da matriz"!), Consulte 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 a ordem de enumerar as propriedades ... não estão especificadas ...

(Ênfase minha.)

Isso significa que, se um navegador quisesse, ele poderia passar pelas propriedades na ordem em que foram inseridos. Ou em ordem numérica. Ou em ordem lexical (onde "30" vem antes de "4"! Lembre-se de que todas as chaves de objetos - e, portanto, todos os índices de array - são na verdade cadeias de caracteres, o que faz todo o sentido). Poderia passar por eles por bucket, se implementasse objetos como tabelas de hash. Ou pegue algo disso e adicione "para trás". Um navegador pode até repetir aleatoriamente e estar em conformidade com ECMA-262, desde que visite cada propriedade exatamente uma vez.

Na prática, a maioria dos navegadores atualmente gosta de iterar aproximadamente na mesma ordem. Mas não há nada dizendo que eles precisam. Isso é específico da implementação e pode mudar a qualquer momento, se for encontrada outra maneira de ser muito mais eficiente.

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

cHao
fonte
18

Principalmente duas razões:

1

Como outros já disseram, você pode obter chaves que não estão no seu array ou que são herdadas do protótipo. Portanto, se, digamos, uma biblioteca adicionar uma propriedade aos protótipos de matriz ou objeto:

Array.prototype.someProperty = true

Você o obterá como parte de toda matriz:

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

você pode resolver isso com o 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
   }
}

mas isso é verdade para iterar sobre qualquer objeto com um loop for-in.

Dois

Geralmente, a ordem dos itens em uma matriz é importante, mas o loop for-in não necessariamente itera na ordem correta, porque trata a matriz como um objeto, que é a maneira como é implementada em JS, e não como uma matriz. Parece uma coisa pequena, mas pode realmente estragar os aplicativos e é difícil de depurar.

Lior
fonte
2
Object.keys(a).forEach( function(item) { console.log(item) } )itere sobre uma matriz de chaves de propriedade próprias, não aquelas herdadas do protótipo.
precisa
2
É verdade, mas como o loop for-in, ele não estará necessariamente na ordem correta do índice. Além disso, ele não funcionará em navegadores antigos que não suportam o ES5.
Lior
Você pode ensinar esses navegadores array.forEachinserindo determinado código em seus scripts. Consulte Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Qwerty
Claro, mas, em seguida, você está manipulando o protótipo, e que nem sempre é uma boa idéia ... e ainda, você tem a questão da ordem ...
Lior
E, é claro, a razão número três: matrizes esparsas.
um oliver melhor
16

Porque enumera através de campos de objetos, não de índices. Você pode obter valor com o índice "length" e duvido que você queira isso.

vava
fonte
Então, qual é a melhor maneira de fazer isso?
LYriCAlsSH 01/02/09
3
para (var i = 0; i <arr.Length; i ++) {}
vava
3
No firefox 3, você também pode usar arr.forEach ou for (var [i, v] no Iterator (arr)) {}, mas nenhum desses funciona no IE, embora você mesmo possa escrever o método forEach.
vava
e praticamente toda biblioteca também possui seu próprio método para isso.
vava
5
Esta resposta está errada. "comprimento" não será incluído na iteração for-in. São apenas as propriedades que você adiciona que estão incluídas.
JacquesB
16

Eu não acho que tenho muito a acrescentar, por exemplo. A resposta do Triptych ou a resposta do CMS sobre por que o uso for...indeve ser evitado em alguns casos.

No entanto, gostaria de acrescentar que nos navegadores modernos existe uma alternativa for...inque pode ser usada nos casos em for...inque não pode ser usada. Essa alternativa é for...of:

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

Nota :

Infelizmente, nenhuma versão do Internet Explorer suporta for...of(o Edge 12 ou superior), portanto, você terá que esperar um pouco mais até poder usá-lo no código de produção do lado do cliente. No entanto, deve ser seguro usar no código JS do lado do servidor (se você usar o Node.js. ).

John Slegers
fonte
@georgeawg Você quis dizer for-of, não for-in, certo?
ᆼ ᆺ ᆼ
15

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 algo assim - é que itera sobre todos os membros de um objeto (bem, todos os membros enumeráveis , mas isso é um detalhe por enquanto). 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, um 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 de matriz, portanto, deve se ater a um índice inteiro. Código que está totalmente ciente do que for ... inrealmente precisa ver todas as propriedades, então tudo bem também.

Pontudo
fonte
Boa explicação Pointy. Apenas curioso. Se eu tivesse uma matriz que estivesse dentro de um objeto com propriedades de multiplicação e for in, em comparação com um loop for regular, essas matrizes seriam iteradas. (o que, em essência, um desempenho lento, certo?)
NiCk Newman
2
@NiCkNewman bem o objeto que você referência depois inem um for ... inloop apenas
Pointy
Eu vejo. Apenas curioso, porque eu tenho objetos e matrizes dentro do meu objeto principal do jogo e queria saber se a entrada seria mais dolorosa do que simplesmente um loop for regular nos índices.
NiCk Newman 31/07/2015
@NiCkNewman bem, o tema de toda essa pergunta é que você não deve usar for ... inmatrizes; existem muitas boas razões para não fazê-lo. Não é tanto uma questão de desempenho, mas uma questão de "certifique-se de que não quebre".
Pointy
Bem, meus objetos são tecnicamente armazenados em uma matriz, por isso fiquei preocupado, algo como [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]:, mas sim, eu entendo.
NiCk Newman 31/07/2015
9

Além disso, devido à semântica, a maneira como for, intrata as matrizes (isto é, o mesmo que qualquer outro objeto JavaScript) não está alinhada com outros idiomas 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
fonte
9

TL&DR: Usar o for inloop em matrizes não é ruim, na verdade, é o contrário.

Eu acho que o for inloop é uma jóia de 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 as desvantagens mencionadas e refutá-las uma a uma.

  1. Ele percorre as propriedades herdadas também: Antes de tudo, qualquer extensão ao Array.prototypedeve ter sido feita usando-se Object.defineProperty()e seu enumerabledescritor deve estar definido como false. Qualquer biblioteca que não esteja fazendo isso não deve ser usada.
  2. As propriedades adicionadas posteriormente à cadeia de herança são contadas: Ao fazer a subclassificação de matrizes por Object.setPrototypeOfou por Classe extend. Você deve novamente usar o Object.defineProperty()que por padrão define os descritores de propriedade writable, enumerablee configurablecomo false. Vamos ver um exemplo de subclassificação de matriz 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ê que o for inloop agora está seguro, pois você se preocupou com o seu código.

  1. O for inciclo é lento: inferno não. É, de longe, o método mais rápido de iteração, se você fizer um loop sobre matrizes esparsas, necessárias de tempos em tempos. Este é um dos truques de desempenho mais importantes que se deve conhecer. Vamos ver um exemplo. Vamos fazer um loop sobre uma matriz esparsa.

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
fonte
@ Ravi Shanker Reddy Boa configuração de benchmarking. Como mencionei na minha resposta, o for inloop ofusca os outros "se" a matriz é escassa e, mais, se fica maior em tamanho. Então, reorganizei o teste de bancada para uma matriz esparsa arr, de tamanho ~ 10000, com apenas 50 itens escolhidos aleatoriamente entre [42,"test",{t:1},null, void 0]os índices aleatórios. Você notará imediatamente a diferença. - >> Confira aqui << - .
Redu
8

Além dos outros problemas, a sintaxe "for..in" é provavelmente mais lenta, porque o índice é uma sequência, não um número 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'
dc1
fonte
Provavelmente não importa muito. Elementos de matriz são propriedades de um objeto com base em Matriz ou semelhante a uma matriz, e todas as propriedades do objeto possuem chaves de sequência. A menos que seu mecanismo JS o otimize de alguma forma, mesmo se você usasse um número, ele acabaria sendo transformado em uma sequência de caracteres para a pesquisa.
cHao 6/06/12
Independentemente de qualquer problema de desempenho, se você é novo no JavaScript, use var i in ae espere que o índice seja um número inteiro; em seguida, faça algo como a[i+offset] = <value>colocar valores completamente nos lugares errados. ("1" + 1 == "11").
Szmoore
8

Um aspecto importante é que for...inapenas itera as propriedades contidas em um objeto cujo atributo de propriedade enumerável esteja definido como true. Portanto, se alguém tentar iterar sobre um objeto usando , propriedades arbitrárias poderão 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 certos elementos não sejam enumerados. Embora, em geral, os atributos de propriedade tendam a se aplicar às propriedades da função dentro de um objeto.for...in

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

myobject.propertyIsEnumerable('myproperty')

Ou para obter todos os quatro atributos de propriedade:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

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

Pierz
fonte
8

O for/ intrabalha 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. Portanto, se você souber que sua matriz é realmente não associativa, poderá usar for (var i=0; i<=arrayLen; i++)e pular a iteração de detecção automática.

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

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]);
Ricardo
fonte
true, a menos que você esteja usando objetos prototipados. ;) abaixo
Ricardo
Isso Arrayé porque Objecttambém
Free Consulting
2
for ... intrabalha com objetos. Não existe detecção automática.
um oliver melhor
7

Porque ele iterará nas propriedades pertencentes aos objetos da cadeia de protótipos, se você não tomar cuidado.

Você pode usar for.. in, apenas verifique cada propriedade com hasOwnProperty .

JAL
fonte
2
Não é suficiente - é perfeitamente aceitável adicionar propriedades nomeadas arbitrárias a instâncias de matriz, e elas serão testadas a truepartir de hasOwnProperty()verificações.
Pointy
Bom ponto, obrigado. Eu nunca fui bobo o suficiente para fazer isso com uma matriz, então não considerei isso!
JAL
1
@ Pointy Eu não testei isso, mas talvez isso possa ser superado usando uma isNaNverificação no nome de cada propriedade.
WynandB
1
@Wynand idéia interessante; no entanto, eu realmente não vejo por que vale a pena tentar iterar com um índice numérico simples.
Pointy
@WynandB desculpe pelo problema, mas sinto que uma correção está em ordem: isNaNé para verificar se uma variável é o valor especial NaN ou não, ela não pode ser usada para verificar outras coisas além de números (você pode usar um método regular typeof para isso).
doldt
6

Não é necessariamente ruim (com base no que você está fazendo), mas no caso de matrizes, se algo foi adicionado Array.prototype, você obterá resultados estranhos. Onde você espera que esse loop seja executado três vezes:

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

Se uma função chamada helpfulUtilityMethodfoi adicionado ao Array's prototype, então o seu ciclo acabaria sendo executado quatro vezes: keyseria 0, 1, 2, e helpfulUtilityMethod. Se você estava esperando apenas números inteiros, opa.

josh3736
fonte
6

Você deve usar o for(var x in y)único nas listas de propriedades, não nos objetos (como explicado acima).

user268396
fonte
13
Apenas uma observação sobre SO - não há 'acima' porque os comentários mudam de ordem na página o tempo todo. Portanto, não sabemos realmente qual comentário você quer dizer. É bom dizer "no comentário de x pessoa" por esse motivo.
JAL
@JAL ... ou adicione o link permanente à resposta.
WynandB
5

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

1.) Já existe uma função ou método de ordem superior que tem esse objetivo 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 executarão uma função apenas uma vez, mas, como uma matriz é enumerada, ela sempre terá um comprimento até o índice mais alto com um valor definido, mas esse comprimento poderá 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. Diferentemente 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 lembre-se exatamente de como eles funcionam. Entenda as condições sob as quais os diferentes loops reiteram, suas funcionalidades separadas e perceba que serão mais ou menos apropriados para diferentes cenários.

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 escrever e tem mais funcionalidade, portanto, você pode adquirir o hábito de usar apenas esse método e padrão, mas seu ligar.

Veja abaixo que os dois primeiros loops executam apenas as instruções console.log uma vez, enquanto o loop for padrão executa a função quantas vezes for especificada, nesse 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
fonte
4

Aqui estão as razões pelas quais essa é (geralmente) uma má prática:

  1. for...inos loops iteram sobre todas as suas próprias propriedades enumeráveis e as propriedades enumeráveis ​​de seus protótipos. Normalmente, em uma iteração de matriz, queremos apenas iterar sobre a própria matriz. E mesmo que você mesmo não adicione nada à matriz, suas bibliotecas ou estrutura podem adicionar algo.

Exemplo :

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...inos loops não garantem uma ordem de iteração específica . Embora a ordem seja vista na maioria dos navegadores modernos atualmente, ainda não há garantia de 100%.
  2. for...inos loops ignoram os undefinedelementos da matriz, isto é, elementos da matriz que ainda não foram atribuídos.

Exemplo :

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
fonte
2

for ... in é útil ao trabalhar em um objeto em JavaScript, mas não em uma matriz, mas ainda não podemos dizer que é um caminho errado, mas não recomendado, veja este exemplo abaixo usando o loop for ... in :

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 criar um protótipo para o Array ...

Array.prototype.someoneelse = "someoneelse";

Agora 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ê a outra pessoa !!! ... Na verdade, estamos fazendo um loop através do 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 ...

Alireza
fonte
2

Um loop for ... in sempre enumera as chaves. As chaves de propriedades dos objetos são sempre String, mesmo as propriedades indexadas de uma matriz:

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

embora não seja especificamente abordado por esta pergunta, eu acrescentaria que há uma boa razão para nunca usar ... com um NodeList(como seria obtido em uma querySelectorAllchamada, pois ele não vê os elementos retornados) iterando apenas sobre as propriedades NodeList.

no caso de um único resultado, obtive:

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

o que explicava porque minha for (node in nodes) node.href = newLink;falha estava.

jcomeau_ictx
fonte