JavaScript per ... in vs per


461

Pensi che ci sia una grande differenza in per ... in e per i loop? Che tipo di "per" preferisci usare e perché?

Diciamo che abbiamo una serie di array associativi:

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

Quindi possiamo iterare:

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

E:

for (var i in myArray)

Non vedo una grande differenza. Ci sono problemi di prestazioni?


13
Si noti che anche noi, come di JS 1.6 , abbiamo: myArray.forEach(callback[, thisarg]).
Benji XVI,

14
@Benji array.forEach è attualmente in ES5.
mikemaccana,

2
in un ciclo for-in hai bisogno di un condizionale che assomigli a questo:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky

6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); è OK da usare praticamente ovunque, tranne in IE8- ed è di gran lunga la sintassi più elegante
Jon z,

5
C'è anche una for...ofdichiarazione nell'ECMAScript 6 , ad esempio:for (let i of myArray) console.log(i);
Vitalii Fedorenko,

Risposte:


548

La scelta dovrebbe basarsi su quale linguaggio sia meglio compreso.

Un array viene ripetuto usando:

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

Un oggetto utilizzato come un array associativo viene ripetuto usando:

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

A meno che tu non abbia ragioni sconvolgenti per la terra, segui il modello di utilizzo stabilito.


38
Va detto che è una buona pratica usare per ... in con il filtraggio dell'istruzione if. Esiste un pratico metodo dell'oggetto "obj.hasOwnProperty (membro)" che controlla se un membro restituito dall'iteratore è effettivamente membro dell'oggetto. Vedi: javascript.crockford.com/code.html
Damir Zekić,

57
Come commentato in un'altra risposta, "for ... in" non funziona correttamente per le matrici, poiché ripeterà tutte le proprietà e i metodi della matrice. Quindi, dovresti usare "for ... in" solo per iterare sulle proprietà degli oggetti. Altrimenti, attenersi a "for (i = 0; i <qualcosa; i ++)"
Denilson Sá Maia

Per motivi di prestazioni, IMO è meglio valutare la lunghezza dell'array prima di for, non valutare a.length ogni volta nel ciclo.
UpTheCreek

9
@UpTheCreek: Questo è sicuramente vero quando l'array è in realtà qualcosa restituito da HTMLDOM, tuttavia, mi chiedo quanto dovrebbe essere grande un array javascript standard prima che tu possa vedere una differenza apprezzabile? Personalmente manterrei il codice il più semplice possibile fino a quando non sarà dimostrato necessario fare qualcosa di diverso.
AnthonyWJones,

2
@Pichan Penso che tu intenda i < l, no i < a, nella tua condizione for-loop.
Max Nanasy,

161

Douglas Crockford raccomanda in JavaScript: le parti buone (pagina 24) per evitare di usare la for indichiarazione.

Se si utilizza for inper scorrere i nomi delle proprietà in un oggetto, i risultati non vengono ordinati. Peggio ancora: potresti ottenere risultati inaspettati; include membri ereditati dalla catena di prototipi e il nome dei metodi.

Tutto tranne le proprietà possono essere filtrati con .hasOwnProperty. Questo esempio di codice fa quello che probabilmente desideravi inizialmente:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}

70
for ... in è perfettamente appropriato per il looping sulle proprietà degli oggetti. Non è appropriato per il looping su elementi di array. Se non riesci a capire la differenza tra questi scenari, allora sì, dovresti evitare per ... in; altrimenti, impazzisci.
Shog9,

8
Vuoi sottolineare il fatto che NON è ORDINATO! Questo potrebbe essere un grosso problema e sarebbe un bug difficile da rilevare.
Jason,

4
+1 per "include membri ereditati dalla catena di prototipi e il nome dei metodi". Ti divertirai se per caso qualcuno usa il tuo codice con Prototype caricato (anche se il tuo codice non lo utilizza effettivamente), per esempio.
ijw

13
Non dimenticare di dichiarare la namevariabile: in caso for(var name in object)...contrario, se quel codice si trova all'interno di una funzione, ad esempio, la namevariabile diventa una proprietà dell'oggetto globale (lo fa un'assegnazione a un identificatore non dichiarato ), anche nel nuovo ECMAScript 5 Modalità rigorosa, quel codice genererà a ReferenceError.
CMS,

6
@Nosredna: c'è un problema relativo all'ordine di iterazione per Chrome, archiviato nientemeno che da John Resig, contrassegnato come WontFix. code.google.com/p/chromium/issues/detail?id=883 . Anche prima di Chrome, l'ordine di iterazione non era lo stesso su tutti i browser se si rimuoveva e si aggiunge nuovamente una proprietà. Anche IE 9 si comporta in modo molto simile a Chrome (presumibilmente per miglioramenti di velocità). Quindi ... Per favore, smetti di diffondere informazioni inesatte, saresti molto ingenuo da mantenere a seconda di esse.
Juan Mendes,

62

Cordiali saluti - Utenti jQuery


Il each(callback)metodo di jQuery utilizza il for( ; ; )loop per impostazione predefinita e verrà utilizzato for( in ) solo se la lunghezza è undefined.

Pertanto, direi che è sicuro assumere l'ordine corretto quando si utilizza questa funzione.

Esempio :

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

L'aspetto negativo di questo è che se stai facendo una logica non UI, le tue funzioni saranno meno portabili su altri framework. La each()funzione è probabilmente meglio riservata all'uso con i selettori jQuery e for( ; ; )potrebbe essere consigliabile diversamente.



4
C'è sempre documentcloud.github.com/underscore che ha _.each e molte altre utili funzioni
w00t

1
significa anche se ho una proprietà length nel mio oggetto $ .each fallirà? ad es. x = {a: "1", b: "2", lunghezza: 3}.
Onur Topal,

29

ci sono differenze di prestazioni a seconda del tipo di loop utilizzato e del browser.

Per esempio:

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

è quasi due volte più veloce su alcuni browser rispetto a:

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

Tuttavia, a meno che i tuoi array non siano ENORMI o non li esegui in loop costantemente, tutti sono abbastanza veloci. Dubito seriamente che il loop array sia un collo di bottiglia nel tuo progetto (o per qualsiasi altro progetto per quella materia)


5
Memorizzare "myArray.length" in una variabile prima del loop farebbe sparire la differenza di prestazioni? La mia ipotesi è "sì".
Tomalak,

3
No. "myArray.length" è una proprietà, non un metodo su un oggetto: non viene eseguito alcun calcolo per determinarne il valore. Memorizzare il suo valore in una variabile non farà nulla.
Jason,

3
Si C'è. Le proprietà non sono variabili; hanno un codice get / set.
ste

12
Tendo a usarefor(var i = myArray.length; i--;)
Kevin,

2
Codice per chiarezza e leggibilità. Non per quello che succede in alcuni browser leggermente più veloce quando si usano array assurdamente enormi nel momento attuale. Le ottimizzazioni possono cambiare, ma la leggibilità del tuo codice (o la sua mancanza) no. Scrivi un codice che gli altri possano seguire facilmente e lascia che gli ottimizzatori riescano a raggiungerlo a tempo debito.
aroth,

26

Si noti che il metodo Array.forEach nativo è ora ampiamente supportato .


2
Che cosa fa? Ha i problemi menzionati in altri post (passando in rassegna le proprietà anziché gli elementi dell'array)? Inoltre, poiché IE8 non lo supporta, è un po 'difficile dire che è ampiamente supportato.
Rauni Lillemets,

2
per quanto gli utenti * nix lo disprezzino, IE8 è soprattutto utenti sub-windows7. questa è una porzione enorme del mercato dei browser.
sbartell,

2
@Rauni - Prendo il tuo punto, ma per i dispositivi desktop la condivisione del browser IE è inferiore al 40%, secondo en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table e secondo markethare.hitslink.com/… e altri siti, almeno l'8% dei browser è IE 9. In altre parole, Array.forEach è supportato da circa il 70% dei browser desktop, quindi non credo che il "supporto esteso" sia irragionevole. Non ho verificato, ma il supporto per dispositivi mobili (sui browser WebKit e Opera) potrebbe essere ancora più elevato. Ovviamente, ci sono notevoli variazioni geografiche.
Sam Dutton,

1
Grazie per l'aggiornamento. Sono d'accordo che si potrebbe dire che è "ampiamente supportato". L'unico problema è che se l'utente utilizza questo metodo JS, deve comunque scrivere un metodo di backup per il caso se non è supportato.
Rauni Lillemets,

1
@Rauni: è possibile utilizzare es5-shim ed es6-shim per fornire automaticamente metodi di backup. github.com/es-shims/es5-shim
Michiel van der Blonk

24

Risposta aggiornata per la versione corrente del 2012 di tutti i principali browser: Chrome, Firefox, IE9, Safari e Opera supportano l'array nativo di ES5.

A meno che tu non abbia qualche motivo per supportare IE8 in modo nativo (tenendo presente che a questi utenti possono essere forniti shim ES5 o Chrome frame, che forniranno un ambiente JS adeguato), è più semplice utilizzare semplicemente la sintassi corretta della lingua:

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

La documentazione completa per array.forEach () è in MDN.


1
Dovresti davvero documentare i parametri del callback: 1 ° il valore dell'elemento, 2 ° l'indice dell'elemento, 3 ° l'array che viene attraversato
sorteggiato il

Ho sentito quello che stai dicendo, ma in questo caso l'eccessiva semplificazione oscura l'intera gamma di possibilità. Avere sia l'indice che il valore significa che può servire da sostituto sia per ... in che per ciascuno ... in - con il bonus che non devi ricordare quale scorre su chiavi o valori.
Drewish,

1
@Cory: ES5 forEach può essere aggiunto abbastanza facilmente ai browser ES3 legacy. Meno codice è un codice migliore.
mikemaccana,

2
@nailer Può essere usato in modo intercambiabile su array e oggetti?
Hitautodestruct

1
@hitautodestruct Fa parte del prototipo di Array, non di Object. In genere, nella comunità in questo momento gli oggetti non array sono ancora ripetuti con 'for (var key in object) {}'.
mikemaccana,

14

I due non sono gli stessi quando l'array è scarso.

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.
}

14

Usare forEach per saltare la catena di prototipi

Solo un breve addendum alla risposta di @ nailer sopra , l'uso di forEach con Object.keys significa che puoi evitare l'iterazione sulla catena di prototipi senza dover utilizzare 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]);
});

2
accidenti, è subdolo. Vale la pena leggere altri 50 post per arrivare a questo. obj = {"pink": "anatre", rosso: "oche"}; Object.keys (obj) === ["pink", "red"]
Orwellophile,

14

In secondo luogo, dovresti scegliere il metodo di iterazione in base alle tue esigenze. Vorrei suggerire che in realtà non eseguire mai il looping nativo Arraycon for instruttura. È molto più lento e , come sottolineato da Chase Seibert al momento, non è compatibile con il framework Prototype.

Esiste un eccellente benchmark su diversi stili di loop che dovresti assolutamente dare un'occhiata se lavori con JavaScript . Non fare ottimizzazioni anticipate, ma dovresti tenere quelle cose da qualche parte nella parte posteriore della tua testa.

Vorrei usare for inper ottenere tutte le proprietà di un oggetto, che è particolarmente utile quando si esegue il debug degli script. Ad esempio, mi piace avere questa riga a portata di mano quando esploro oggetti sconosciuti:

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

Scarica il contenuto dell'intero oggetto (insieme ai corpi del metodo) nel mio registro Firebug. Molto utile.


Il collegamento ora è interrotto. Sicuramente vorrebbe vedere il benchmark, se qualcuno ha un altro link.
Billbad,

Non è più rotto.
Olli,

I loop foreach si rompono nel prototipo? Dato che ora è comunemente supportato, questo è qualcosa che il prototipo dovrebbe risolvere.
mvrak,

7

ecco qualcosa che ho fatto.

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

ecco come lo
useresti, funzionerà su array e oggetti (come un elenco di elementi 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';
  }
});

Ho appena fatto questo, quindi sono aperto a suggerimenti :)


Roba fantastica - piuttosto semplice!
Chris,

6

Userei i diversi metodi in base al modo in cui volevo fare riferimento agli articoli.

Usa foreach se vuoi solo l'oggetto corrente.

Utilizzare per se è necessario un indicizzatore per eseguire confronti relativi. (Cioè come si confronta con l'elemento precedente / successivo?)

Non ho mai notato una differenza di prestazioni. Aspetterei di avere un problema di prestazioni prima di preoccuparmene.


Vedi la risposta di Bnos qui sotto - perché ... non sta facendo quello che ti aspetti qui e se lo usi potresti divertirti di tutti i tipi. Per la cronaca, Prototype fa le cose nel modo giusto .
marcus.greasly,

4

Con for (var i in myArray) è possibile ciclare su oggetti troppo, i conterrà il nome della chiave ed è possibile accedere alla proprietà tramite myArray [i] . Additionaly, i metodi avrete aggiunto all'oggetto saranno inclusi nel ciclo, anche, per esempio, se si utilizza qualsiasi quadro esterno come jQuery o di un prototipo, o se si aggiungono i metodi per prototipi degli oggetti direttamente, ad un certo punto mi farò puntare a quei metodi.


4

Attento!

Se hai diversi tag di script e stai cercando informazioni negli attributi dei tag, ad esempio, devi usare la proprietà .length con un ciclo for perché non è un array semplice ma un oggetto HTMLCollection.

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

Se usi l'istruzione foreach per (var i in yourList), nella maggior parte dei browser verranno restituiti proterties e metodi di HTMLCollection!

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!

Anche se getElementsByTagName deve restituire un NodeList, la maggior parte dei browser restituisce un HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName


3

Perché i loop in array non sono compatibili con Prototype. Se pensi di dover utilizzare quella libreria in futuro, sarebbe logico attenersi ai loop.

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


Dimentica "potrebbe essere necessario utilizzare quella libreria". Pensa invece "il tuo JS potrebbe essere incluso in qualsiasi altra cosa che utilizza quella libreria", perché i problemi continuano.
ijw

3

Ho riscontrato problemi con "per ciascuno" utilizzando oggetti, prototipi e array

la mia comprensione è che il per ciascuno è per proprietà di oggetti e NON matrici


3

Se vuoi davvero velocizzare il tuo codice, che ne pensi?

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

è una specie di logica while all'interno dell'istruzione for ed è meno ridondante. Anche Firefox ha Array.forEach e Array.filter


2
Perché questo dovrebbe accelerare il tuo codice? Non riesco a capire perché affermazioni in questo modo accelerino.
Rup,

3

Un codice più breve e migliore secondo jsperf è

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

1

Utilizzare il ciclo Array (). ForEach per sfruttare il parallelismo


4
JavaScript nel browser è simultaneo al ciclo di eventi, quindi Array.prototype.forEachnon eseguirà più chiamate al callback in parallelo.
Mike Samuel,

1

per (;;) è per array : [20,55,33]

for..in è per Oggetti : {x: 20, y: 55: z: 33}


0

Stai attento!!! Sto usando Chrome 22.0 in Mac OS e sto riscontrando problemi con la sintassi per ciascuna.

Non so se questo è un problema del browser, un problema javascript o qualche errore nel codice, ma è MOLTO strano. Al di fuori dell'oggetto funziona perfettamente.

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

0

C'è una differenza importante tra entrambi. Il for-in scorre le proprietà di un oggetto, quindi quando il case è un array, non solo itererà sui suoi elementi ma anche sulla funzione "rimuovi" che ha.

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

È possibile utilizzare il for-in con un if(myArray.hasOwnProperty(i)). Tuttavia, quando si scorre su array, preferisco sempre evitarlo e usare semplicemente l'istruzione for (;;).


0

Sebbene entrambi siano molto simili c'è una differenza minore:

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
}

in questo caso l'output è:

STANDARD PER 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
}

mentre in questo caso l'output è:

LOOP FOR-IN:

ARRAY [1] = B

ARRAY [2] = C

ARRAY [10] = D

ARRAY [ABC] = 123


0

L'istruzione for consente di scorrere ciclicamente i nomi di tutte le proprietà di un oggetto. Sfortunatamente, scorre anche attraverso tutti i membri che sono stati ereditati attraverso la catena di prototipi. Ciò ha il cattivo effetto collaterale di servire le funzioni del metodo quando l'interesse è nei membri dei dati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.