JavaScript para… em vs para

461

Você acha que há uma grande diferença em ... em e para loops? Que tipo de "para" você prefere usar e por quê?

Digamos que temos uma matriz de matrizes associativas:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Para que possamos iterar:

for (var i = 0; i < myArray.length; i++)

E:

for (var i in myArray)

Não vejo grande diferença. Existem problemas de desempenho?

andrii
fonte
13
Note-se que, também nós, como de JS 1.6 , tem-se: myArray.forEach(callback[, thisarg]).
Benji XVI
14
@Benji array.forEach está realmente no ES5.
Mckmaccana
2
em um loop for-in, você precisa de um condicional parecido com o seguinte:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky 27/03
6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); é OK para usar praticamente em todos os lugares, exceto em IE8- e é de longe o mais elegante sintaxe
Jon z
5
Há também uma for...ofdeclaração no ECMAScript 6 , por exemplo:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Respostas:

548

A escolha deve basear-se em qual idioma é melhor compreendido.

Uma matriz é iterada usando:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

Um objeto usado como uma matriz associativa é iterado usando:

for (var key in o)
  //do stuff with o[key]

A menos que você tenha razões que abalem a terra, siga o padrão estabelecido de uso.

AnthonyWJones
fonte
38
Deve-se mencionar que é uma boa prática usar para ... in com a filtragem if. Existe um método prático do objeto "obj.hasOwnProperty (member)" que verifica se um membro retornado pelo iterador é realmente membro do objeto. Veja: javascript.crockford.com/code.html
Damir Zekić
57
Conforme comentado em outra (s) resposta (s), o "para ... em" não funciona corretamente para matrizes, pois ele iterará sobre todas as propriedades e métodos da matriz. Portanto, você deve usar "for ... in" apenas para iterar sobre as propriedades do objeto. Caso contrário, atenha-se a "for (i = 0; i <alguma coisa; i ++)"
Denilson Sá Maia
Por motivos de desempenho, o IMO é melhor avaliar o comprimento da matriz antes de for, não avaliar a.length cada vez no loop.
22812 UpTheCreek
9
@UpTheCreek: Isso é definitivamente verdade quando a matriz é de fato algo retornado pelo HTMLDOM, no entanto, eu me pergunto quão grande seria uma matriz javascript padrão antes que você pudesse ver uma diferença significativa. Pessoalmente, manteria o código o mais simples possível, até que seja necessário fazer algo diferente.
AnthonyWJones 22/03
2
@ Pichan Eu acho que você quer dizer i < l, não i < a, na sua condição de loop for.
precisa saber é o seguinte
161

Douglas Crockford recomenda em JavaScript: The Good Parts (página 24) para evitar o uso da for indeclaração.

Se você usar o for inloop sobre nomes de propriedades em um objeto, os resultados não serão ordenados. Pior: você pode obter resultados inesperados; inclui membros herdados da cadeia de protótipos e o nome dos métodos.

Tudo, exceto as propriedades, pode ser filtrado com .hasOwnProperty. Este exemplo de código faz o que você provavelmente queria originalmente:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}
Benno Richters
fonte
70
for ... in é perfeitamente apropriado para fazer loop sobre as propriedades do objeto. Não é apropriado para repetir elementos da matriz. Se você não consegue entender a diferença entre esses cenários, sim, você deve evitar por ... in; caso contrário, enlouqueça.
Shog9
8
Deseja enfatizar o fato de que NÃO É ENCOMENDADO! Este poderia ser um grande problema e seria um problema difícil de detectar.
4179 Jason
4
+1 para "inclui membros herdados da cadeia de protótipos e o nome dos métodos". Você se divertirá se alguém usar seu código com o Prototype carregado (mesmo que seu código não o use de fato), por exemplo.
Ijw 5/08/09
13
Não se esqueça de declarar a namevariável: for(var name in object)...caso contrário, se esse código estiver dentro de uma função, por exemplo, a namevariável acabará sendo uma propriedade do objeto global (uma atribuição a um identificador não declarado faz isso), também no novo ECMAScript 5 Modo estrito, esse código lançará a ReferenceError.
CMS
6
@Nosredna: Há um problema sobre a ordem de iteração do Chrome, arquivada por ninguém menos que John Resig, marcada como WontFix. code.google.com/p/chromium/issues/detail?id=883 . Mesmo antes do chrome, a ordem da iteração não era a mesma nos navegadores se você remover e adicionar uma propriedade novamente. Também o IE 9 se comporta muito como o chrome (supostamente para melhorias de velocidade). Então ... Por favor, pare de espalhar informações imprecisas, você seria muito ingênuo, dependendo disso.
Juan Mendes
62

FYI - Usuários do jQuery


O each(callback)método do jQuery usa for( ; ; )loop por padrão e será usado for( in ) apenas se o comprimento for undefined.

Portanto, eu diria que é seguro assumir a ordem correta ao usar esta função.

Exemplo :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

A desvantagem de usar isso é que, se você estiver executando alguma lógica que não seja da interface do usuário, suas funções serão menos portáteis para outras estruturas. A each()função provavelmente é melhor reservada para uso com seletores jQuery e for( ; ; )pode ser aconselhável caso contrário.


Jason
fonte
4
Sempre existe o documentcloud.github.com/underscore que possui _.each e várias outras funções úteis
w00t
1
também significa que se eu tiver uma propriedade length no meu objeto $ .each falhará? por exemplo, x = {a: "1", b: "2", comprimento: 3}.
Onur Topal
29

existem diferenças de desempenho, dependendo do tipo de loop que você usa e em qual navegador.

Por exemplo:

for (var i = myArray.length-1; i >= 0; i--)

é quase duas vezes mais rápido em alguns navegadores do que:

for (var i = 0; i < myArray.length; i++)

No entanto, a menos que suas matrizes sejam ENORME ou você as repita constantemente, todas são rápidas o suficiente. Eu duvido seriamente que o loop de matriz seja um gargalo no seu projeto (ou em qualquer outro projeto)

Gene
fonte
5
Armazenar "myArray.length" em uma variável antes do loop faria a diferença de desempenho desaparecer? Meu palpite é "sim".
Tomalak
3
Não. 'MyArray.length' é uma propriedade, não um método em um objeto - nenhum cálculo é feito para determinar seu valor. Armazenar seu valor em uma variável não fará nada.
jason
3
Sim existe. Propriedades não são variáveis; eles têm código get / set.
ste 25/05
12
I tendem a usarfor(var i = myArray.length; i--;)
Kevin
2
Código para maior clareza e legibilidade. Não é o que acontece correr marginalmente mais rápido em alguns navegadores ao usar matrizes absurdamente grandes no momento atual. As otimizações podem mudar, mas a legibilidade do seu código (ou a falta dela) não. Escreva um código que outras pessoas possam seguir facilmente e permita que os otimizadores se atualizem no devido tempo.
Aroth 27/04
26

Observe que o método Array.forEach nativo agora é amplamente suportado .

Sam Dutton
fonte
2
O que isso faz? Ele tem os problemas mencionados em outras postagens (repetindo propriedades em vez de elementos da matriz)? Além disso, como o IE8 não suporta, é um pouco difícil dizer que é amplamente suportado.
Rauni Lillemets
2
por mais que os usuários * nix o desprezem, o IE8 é a maioria de todos os usuários do sub-windows7. isso é uma grande parte do mercado de navegadores.
sbartell
2
@Rauni - Entendo, mas para dispositivos de desktop, o compartilhamento do navegador IE é inferior a 40%, de acordo com en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table e de acordo com o marketshare.hitslink.com/… e outros sites, pelo menos 8% dos navegadores são o IE 9. Em outras palavras, o Array.forEach é suportado por cerca de 70% dos navegadores de desktop; portanto, não acho que 'amplamente suportado' seja irracional. Não verifiquei, mas o suporte móvel (nos navegadores WebKit e Opera) pode ser ainda maior. Obviamente, existem variações consideráveis ​​geograficamente.
Sam Dutton
1
Obrigado pela atualização. Concordo que se poderia dizer que é "amplamente apoiado". O único problema é que se o usuário utiliza este método JS, ele / ela ainda tem que escrever um método de back-up para o caso se ele não é suportado ..
Rauni Lillemets
1
@Rauni - você pode usar es5-shim e es6-shim para fornecer automaticamente métodos de backup. github.com/es-shims/es5-shim
Michiel van der Blonk
24

Resposta atualizada para a versão atual de 2012 de todos os principais navegadores - Chrome, Firefox, IE9, Safari e Opera suportam o array.forEach nativo do ES5.

A menos que você tenha algum motivo para oferecer suporte ao IE8 nativamente (tendo em mente que o ES5-shim ou o quadro Chrome pode ser fornecido a esses usuários, o que fornecerá um ambiente JS adequado), é mais fácil usar simplesmente a sintaxe adequada do idioma:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

A documentação completa do array.forEach () está no MDN.

mikemaccana
fonte
1
Você realmente deve documentar os parâmetros da chamada de retorno: 1 o valor do elemento, 2º o índice do elemento, 3 a matriz a ser percorrido
drewish
Ouvi o que você está dizendo, mas, neste caso, simplificar demais obscurece toda a gama de possibilidades. Ter o índice e o valor significa que ele pode servir como um substituto para ... em e para cada ... em - com o bônus de que você não precisa se lembrar de que itera sobre chaves ou valores.
drewish
1
@Cory: o ES5 forEach pode ser adicionado aos navegadores ES3 herdados com bastante facilidade. Menos código é melhor.
Mikemaccana 27/07/2012
2
@nailer Isso pode ser usado em matrizes e objetos de forma intercambiável?
precisa saber é o seguinte
1
@hitautodestruct Faz parte do protótipo do Array, não do Object. Geralmente na comunidade, no momento, os objetos que não são da matriz ainda são iterados com 'for (chave var no objeto) {}'.
Mikemaccana # 03
14

Os dois não são os mesmos quando a matriz é escassa.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}
Mike Samuel
fonte
14

Usando o forEach para pular a cadeia de protótipos

Apenas um adendo rápido à resposta do @ nailer acima , usando forEach com Object.keys significa que você pode evitar a iteração na cadeia de protótipos sem precisar usar o hasOwnProperty.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});
meloncholy
fonte
2
caramba, isso é sorrateiro. Valeu a pena ler outras 50 postagens para chegar a isso. obj = {"rosa": "patos", vermelho: "gansos"}; Object.keys (obj) === ["rosa", "vermelho"]
Orwellophile 10/07/12
14

Tenho uma segunda opinião de que você deve escolher o método de iteração de acordo com sua necessidade. Eu sugiro que você nunca faça loop nativo Arraycom a for inestrutura. É muito mais lento e , como Chase Seibert apontou no momento atrás, não é compatível com o framework Prototype.

Há uma excelente referência em diferentes estilos de loop que você absolutamente deve observar se trabalhar com JavaScript . Não faça otimizações precoces, mas você deve guardar essas coisas em algum lugar na sua cabeça.

Eu usaria for inpara obter todas as propriedades de um objeto, o que é especialmente útil ao depurar seus scripts. Por exemplo, eu gosto de ter essa linha à mão quando eu explorar objetos desconhecidos:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Ele despeja o conteúdo de todo o objeto (junto com os corpos dos métodos) no meu log do Firebug. Muito útil.

Damir Zekić
fonte
Link agora está quebrado. Com certeza, gostaria de ver a referência, se alguém tiver outro link.
Billbad
Não está mais quebrado.
Olli
Loops foreach quebram em protótipo? Como agora é comumente suportado, isso é algo que o protótipo deve resolver.
mvrak
7

Aqui está algo que eu fiz.

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

é assim que você usaria,
isso funcionará em matrizes e objetos (como uma lista de elementos HTML)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Acabei de fazer isso, estou aberto a sugestões :)


fonte
Ótimas coisas - bem simples!
22411 Chris
6

Eu usaria os diferentes métodos com base em como eu queria fazer referência aos itens.

Use foreach se quiser apenas o item atual.

Use para se você precisar de um indexador para fazer comparações relativas. (Ou seja, como isso se compara ao item anterior / próximo?)

Eu nunca notei uma diferença de desempenho. Eu esperaria até ter um problema de desempenho antes de me preocupar com isso.

Matt Lacey
fonte
Veja a resposta Bnos abaixo - pois ... não está fazendo o que você espera aqui e, se você usá-lo, pode muito bem se divertir. Para o registro, Prototype faz as coisas da maneira certa .
Marcus.greasly 28/10/08
4

Com for (var i em myArray) você pode loop sobre objetos também, i irá conter o nome da chave e você pode acessar a propriedade via myArray [i] . Adicionalmente, quaisquer métodos que você terá adicionado ao objeto será incluído no circuito, também, ou seja, se você usar qualquer estrutura externa como jQuery ou protótipo, ou se você adicionar métodos para protótipos de objetos diretamente, em um ponto i irá apontar para esses métodos.

pilsetnieks
fonte
4

Cuidado!

Se você tiver várias tags de script e estiver pesquisando informações nos atributos da tag, por exemplo, precisará usar a propriedade .length com um loop for, porque não é uma matriz simples, mas um objeto HTMLCollection.

https://developer.mozilla.org/en/DOM/HTMLCollection

Se você usar a instrução foreach para (var i em yourList), ela retornará propriedades e métodos do HTMLCollection na maioria dos navegadores!

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

Mesmo se getElementsByTagName devolver um NodeList, a maioria dos navegadores retornará uma HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName

baptx
fonte
3

Para loops em matrizes não é compatível com o protótipo. Se você acha que pode precisar usar essa biblioteca no futuro, faria sentido manter loops.

http://www.prototypejs.org/api/array

Chase Seibert
fonte
Esqueça "você pode precisar usar essa biblioteca". Em vez disso, pense que "seu JS pode estar incluído em qualquer outra coisa que use essa biblioteca", porque os problemas ainda aparecem.
Ijw 5/08/09
3

Eu já vi problemas com o "para cada" usando objetos e protótipo e matrizes

meu entendimento é que o para cada um é para propriedades de objetos e não matrizes

Benjamin Lee
fonte
3

Se você realmente deseja acelerar o seu código, e quanto a isso?

for( var i=0,j=null; j=array[i++]; foo(j) );

é meio que ter a lógica while dentro da instrução for e é menos redundante. O firefox também possui Array.forEach e Array.filter

fabjoa
fonte
2
Por que isso aceleraria seu código? Não vejo por que gravar declarações como essa aceleraria.
Rup
3

Um código mais curto e melhor de acordo com jsperf é

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}
Bormat
fonte
1

Use o loop Array (). ForEach para aproveitar o paralelismo

PazoozaTest Pazman
fonte
4
O JavaScript no navegador é um loop de eventos simultâneo, portanto Array.prototype.forEach, não executará várias chamadas para o retorno de chamada em paralelo.
Mike Samuel
1

for (;;) é para matrizes : [20,55,33]

for..in é para Objetos : {x: 20, y: 55: z: 33}

angelito
fonte
0

Seja cuidadoso!!! Estou usando o Chrome 22.0 no Mac OS e estou tendo problemas com o para cada sintaxe.

Não sei se este é um problema do navegador, um problema de javascript ou algum erro no código, mas é MUITO estranho. Fora do objeto, ele funciona perfeitamente.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works
Paulo Check
fonte
0

Há uma diferença importante entre os dois. O for-in itera sobre as propriedades de um objeto, portanto, quando o caso é uma matriz, ele não apenas itera sobre seus elementos, mas também sobre a função "remover" que possui.

for (var i = 0; i < myArray.length; i++) { 
    console.log(i) 
}

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

Você pode usar o for-in com um if(myArray.hasOwnProperty(i)). Ainda assim, ao iterar sobre matrizes, eu sempre prefiro evitar isso e apenas usar a instrução for (;;).

achecopar
fonte
0

Embora os dois sejam muito parecidos, há uma pequena diferença:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

neste caso, a saída é:

PADRÃO PARA LOOP:

ARRAY [0] = A

ARRAY [1] = B

ARRAY [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

enquanto neste caso a saída é:

LOOP FOR-IN:

ARRAY [1] = B

ARRAY [2] = C

ARRAY [10] = D

ARRAY [ABC] = 123

Yash Srivastava
fonte
0

A instrução for in permite fazer um loop pelos nomes de todas as propriedades de um objeto. Infelizmente, ele também percorre todos os membros que foram herdados pela cadeia de protótipos. Isso tem o efeito colateral ruim de atender às funções do método quando o interesse está nos membros dos dados.

Fayaz
fonte