Perché usare "for ... in" con l'iterazione dell'array è una cattiva idea?


1824

Mi è stato detto di non utilizzare gli for...inarray in JavaScript. Perchè no?


45
Ho visto la recente domanda in cui qualcuno te lo ha detto, ma hanno significato solo per gli array. È considerata una cattiva pratica per iterare attraverso gli array ma non necessariamente per iterare attraverso i membri di un oggetto.
mmurch

19
Molte risposte con loop "for" come 'for (var i = 0; i <hColl.length; i ++) {}' confronta con 'var i = hColl.length; mentre (i--) {} 'che, quando è possibile utilizzare quest'ultima forma, è sostanzialmente più veloce. So che è tangenziale, ma ho pensato di aggiungere questo bit.
Mark Schultheiss,

2
@MarkSchultheiss ma questa è iterazione inversa. Esiste un'altra versione di iterazione diretta più veloce?
ma11hew28,

5
@Wynand usa la var i = hCol1.length; for (i;i;i--;) {}cache in quanto farà la differenza e semplifica il test. - quanto più vecchio è il browser, tanto maggiore è la differenza tra fore whileSEMPRE memorizza il contatore "i" - e ovviamente il negativo non sempre si adatta alla situazione, e il negativo mentre obfuscate il codice è un po 'per alcune persone. e nota var i = 1000; for (i; i; i--) {}e var b =1000 for (b; b--;) {}dove vado da 1000 a 1 eb va da 999 a 0. - più vecchio è il browser, più il tempo tende a favorire le prestazioni.
Mark Schultheiss,

9
Puoi anche essere intelligente. for(var i = 0, l = myArray.length; i < l; ++i) ...è il più veloce e il migliore che puoi ottenere con l'iterazione diretta.
Mathieu Amiot,

Risposte:


1557

Il motivo è che un costrutto:

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 volte può essere totalmente diverso dall'altro:

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

Considera anche che le librerie JavaScript potrebbero fare cose del genere, che influenzeranno qualsiasi array creato:

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


146
Storicamente, alcuni browser hanno persino ripetuto "lunghezza", "toString" ecc.!
mercoledì

398
Ricorda di usare (var x in a)piuttosto che (x in a)- non vuoi creare un globale.
Chris Morgan,

78
Il primo numero non è un motivo per cui è negativo, ma solo una differenza nella semantica. Il secondo problema mi sembra un motivo (oltre agli scontri tra le librerie che fanno lo stesso) che alterare il prototipo di un tipo di dati incorporato è un male, piuttosto che per ... in un male.
Stewart

86
@Stewart: tutti gli oggetti in JS sono associativi. Un array JS è un oggetto, quindi sì, è anche associativo, ma non è per questo. Se si desidera scorrere le chiavi di un oggetto , utilizzare for (var key in object). Se si desidera scorrere gli elementi di un array , tuttavia, utilizzare for(var i = 0; i < array.length; i += 1).
Martijn

42
Hai detto per il primo esempio, che scorre su indici numerici da 0 a 4, come tutti si aspettano , mi aspetto che passi da 0 a 5 ! Poiché se si aggiunge un elemento in posizione 5, l'array avrà 6 elementi (5 dei quali non definiti).
stivlo,

393

L' for-inaffermazione di per sé non è una "cattiva pratica", tuttavia può essere utilizzata in modo errato , ad esempio, per scorrere su array o oggetti simili a array.

Lo scopo for-indell'istruzione è di enumerare le proprietà dell'oggetto. Questa affermazione salirà nella catena del prototipo, enumerando anche le proprietà ereditate , cosa che a volte non è desiderata.

Inoltre, l'ordine di iterazione non è garantito dalla specifica. Ciò significa che se si desidera "iterare" un oggetto array, con questa affermazione non si può essere certi che le proprietà (indici di array) saranno visitate in ordine numerico.

Ad esempio, in JScript (IE <= 8), l'ordine di enumerazione anche sugli oggetti Array viene definito quando sono state create le proprietà:

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
}

Inoltre, parlando di proprietà ereditate, se, ad esempio, estendi l' Array.prototypeoggetto (come alcune librerie come fanno MooTools), verranno enumerate anche quelle proprietà:

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

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

Come ho detto prima per scorrere su array o oggetti simili a array, la cosa migliore è usare un loop sequenziale , come un semplice for/ vecchio whileloop.

Quando si desidera enumerare solo le proprietà proprie di un oggetto (quelle che non sono ereditate), è possibile utilizzare il hasOwnPropertymetodo:

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

E alcune persone consigliano persino di chiamare il metodo direttamente da Object.prototypeper evitare problemi se qualcuno aggiunge una proprietà denominata hasOwnPropertyal nostro oggetto:

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

10
Vedi anche il post di David Humphrey Iterazione rapida degli oggetti in JavaScript rapidamente - poiché l'array for..inè molto più lento dei loop "normali".
Chris Morgan,

17
Domanda sull'ultimo punto su "hasOwnProperty": Se qualcuno sostituisce "hasOwnProperty" su un oggetto, avrai problemi. Ma non avrai gli stessi problemi se qualcuno sovrascrive "Object.prototype.hasOwnProperty"? Ad ogni modo ti stanno rovinando e non è una tua responsabilità, giusto?
Scott Rippey,

Dici che for..innon è una cattiva pratica, ma può essere utilizzata in modo improprio. Hai un esempio reale di buone pratiche, in cui volevi davvero esaminare tutte le proprietà di un oggetto, comprese le proprietà ereditate?
rjmunro,

4
@ScottRippey: se vuoi portarlo lì: youtube.com/watch?v=FrFUI591WhI
Nathan Wall

con questa risposta ho scoperto che è possibile accedere al valore confor (var p in array) { array[p]; }
equiman

117

Esistono tre motivi per cui non è necessario utilizzare for..inper scorrere gli elementi dell'array:

  • for..inpasserà in rassegna tutte le proprietà proprie ed ereditate dell'oggetto array che non lo sono DontEnum; ciò significa che se qualcuno aggiunge proprietà allo specifico oggetto array (ci sono validi motivi per questo - l'ho fatto da solo) o modificato Array.prototype(che è considerato una cattiva pratica nel codice che dovrebbe funzionare bene con altri script), queste proprietà essere ripetuto anche; le proprietà ereditate possono essere escluse controllando hasOwnProperty(), ma ciò non ti aiuterà con le proprietà impostate nell'oggetto array stesso

  • for..in non è garantito per preservare l'ordinamento degli elementi

  • è lento perché devi percorrere tutte le proprietà dell'oggetto array e la sua intera catena di prototipi e otterrai comunque solo il nome della proprietà, cioè per ottenere il valore, sarà richiesta una ricerca aggiuntiva


55

Perché per ... in enumera attraverso l'oggetto che contiene l'array, non l'array stesso. Se aggiungo una funzione alla catena di prototipi degli array, anche quella verrà inclusa. ie

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

Questo scriverà:

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

E poiché non si può mai essere sicuri che nulla verrà aggiunto alla catena di prototipi, basta usare un ciclo for per enumerare l'array:

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

Questo scriverà:

0 = pippo
1 = bar

16
Le matrici sono oggetti, non esiste un "oggetto che contiene la matrice".
RobG

41

In isolamento, non c'è nulla di sbagliato nell'uso del for-in sugli array. For-in scorre i nomi delle proprietà di un oggetto e, nel caso di un array "out-of-the-box", le proprietà corrispondono agli indici dell'array. (Il built-in propertes come length, toStringe così via non sono inclusi nella iterazione.)

Tuttavia, se il codice (o il framework in uso) aggiunge proprietà personalizzate agli array o al prototipo di array, tali proprietà verranno incluse nell'iterazione, che probabilmente non è ciò che si desidera.

Alcuni framework JS, come Prototype, modificano il prototipo di array. Altri framework come JQuery no, quindi con JQuery puoi usare in sicurezza il for-in.

In caso di dubbi, probabilmente non dovresti usare il for-in.

Un modo alternativo di iterare attraverso un array è usare un ciclo for:

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

Tuttavia, questo ha un problema diverso. Il problema è che un array JavaScript può avere "buchi". Se si definisce arrcome:

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

Quindi l'array ha due elementi, ma una lunghezza di 101. L'uso di for-in produrrà due indici, mentre il ciclo for produrrà 101 indici, dove 99 ha un valore di undefined.


37

A partire dal 2016 (ES6) potremmo utilizzare for…ofper l'iterazione dell'array, come già notato da John Slegers.

Vorrei solo aggiungere questo semplice codice dimostrativo, per chiarire le cose:

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

La console mostra:

for...of:

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

for...in:

0: 5
1: foo

In altre parole:

  • for...ofconta da 0 a 5 e ignora anche Array.prototype.foo. Mostra i valori dell'array .

  • for...inelenca solo gli 5, ignorando gli indici di array non definiti, ma aggiungendo foo. Mostra i nomi delle proprietà dell'array .


32

Risposta breve: non ne vale la pena.


Risposta più lunga: non ne vale la pena, anche se non sono richiesti un ordine sequenziale degli elementi e prestazioni ottimali.


Risposta lunga: non ne vale la pena ...

  • L'uso for (var property in array)farà sì arrayche venga ripetuto come oggetto , attraversando la catena di prototipi di oggetti e alla fine eseguendo più lentamente di un forciclo basato su indice .
  • for (... in ...) non è garantito per restituire le proprietà dell'oggetto in ordine sequenziale, come ci si potrebbe aspettare.
  • L'uso hasOwnProperty()e i !isNaN()controlli per filtrare le proprietà dell'oggetto sono un ulteriore sovraccarico che lo rende ancora più lento e annulla il motivo chiave per usarlo in primo luogo, ovvero a causa del formato più conciso.

Per questi motivi non esiste nemmeno un compromesso accettabile tra prestazioni e convenienza. Non c'è davvero alcun vantaggio a meno che l'intento non sia gestire l'array come oggetto ed eseguire operazioni sulle proprietà dell'oggetto dell'array.


31

Oltre ai motivi indicati in altre risposte, potresti non voler utilizzare la struttura "for ... in" se devi fare matematica con la variabile counter perché il ciclo scorre attraverso i nomi delle proprietà dell'oggetto e quindi la variabile è una stringa.

Per esempio,

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

scriverò

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

mentre,

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

scriverò

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

Naturalmente, questo può essere facilmente superato includendo

ii = parseInt(ii);

nel ciclo, ma la prima struttura è più diretta.


6
Puoi usare il prefisso +invece parseIntche a meno che tu non abbia davvero bisogno di numeri interi o ignori caratteri non validi.
Konrad Borowski,

Inoltre, l'uso parseInt()non è raccomandato. Prova parseInt("025");e avrà esito negativo.
Derek 朕 會 功夫 il

6
@Derek 朕 會 功夫 - puoi sicuramente usarlo parseInt. Il problema è che se non includi il radix, i browser più vecchi potrebbero tentare di interpretare il numero (quindi 025 diventa ottale). Questo problema è stato risolto in ECMAScript 5 ma succede ancora per i numeri che iniziano con "0x" (interpreta il numero come esadecimale). Per essere al sicuro, usa il radix per specificare il numero in questo modo parseInt("025", 10)- che specifica la base 10.
IAmTimCorey

23

A parte il fatto che for... inscorre su tutte le proprietà enumerabili (che non è lo stesso di "tutti gli elementi dell'array"!), Vedi http://www.ecma-international.org/publications/files/ECMA-ST/Ecma -262.pdf , sezione 12.6.4 (5a edizione) o 13.7.5.15 (7a edizione):

La meccanica e l' ordine di enumerazione delle proprietà ... non sono specificati ...

(Enfasi mia.)

Ciò significa che se un browser lo desiderasse, potrebbe passare attraverso le proprietà nell'ordine in cui sono state inserite. O in ordine numerico. O in ordine lessicale (dove "30" precede "4"! Ricorda che tutte le chiavi degli oggetti - e quindi tutti gli indici degli array - sono in realtà stringhe, quindi ha un senso totale). Potrebbe passarli attraverso bucket, se implementasse oggetti come tabelle hash. Oppure prendi uno di questi e aggiungi "indietro". Un browser potrebbe anche iterare in modo casuale ed essere conforme a ECMA-262, purché visitasse ciascuna proprietà esattamente una volta.

In pratica, alla maggior parte dei browser attualmente piace iterare all'incirca nello stesso ordine. Ma non c'è niente da dire che devono. Questo è specifico per l'implementazione e potrebbe cambiare in qualsiasi momento se un altro modo fosse ritenuto molto più efficiente.

Ad ogni modo, for... non inporta con sé nessuna connotazione di ordine. Se ti interessa l'ordine, sii esplicito a riguardo e usa un forciclo regolare con un indice.


18

Principalmente due motivi:

Uno

Come altri hanno già detto, potresti ottenere chiavi che non sono nel tuo array o che sono ereditate dal prototipo. Quindi, diciamo, una libreria aggiunge una proprietà ai prototipi di array o oggetti:

Array.prototype.someProperty = true

Lo otterrai come parte di ogni array:

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

potresti risolverlo con il metodo hasOwnProperty:

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

ma questo è vero per iterare su qualsiasi oggetto con un ciclo for-in.

Due

Di solito l'ordine degli elementi in un array è importante, ma il ciclo for-in non itererà necessariamente nell'ordine giusto, perché tratta l'array come un oggetto, che è il modo in cui è implementato in JS, e non come un array. Sembra una cosa piccola, ma può davvero rovinare le applicazioni ed è difficile eseguire il debug.


2
Object.keys(a).forEach( function(item) { console.log(item) } )iterare su un array di proprie chiavi di proprietà, non quelle ereditate dal prototipo.
Qwerty

2
Vero, ma come il ciclo for-in, non sarà necessariamente nell'ordine corretto dell'indice. Inoltre, non funzionerà su browser meno recenti che non supportano ES5.
Lior,

Puoi insegnare a quei browser array.forEachinserendo un certo codice nei tuoi script. Vedi Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Qwerty

Certo, ma poi stai manipolando il prototipo, e non è sempre una buona idea ... e comunque hai il problema dell'ordine ...
Lior

E, naturalmente, la ragione numero tre: array sparsi.
un oliver migliore il

16

Perché enumera attraverso i campi oggetto, non gli indici. Puoi ottenere valore con l'indice "lunghezza" e dubito che tu voglia questo.


Qual è il modo migliore per farlo?
lYriCAlsSH

3
per (var i = 0; i <arr.length; i ++) {}
vava

3
In firefox 3 potresti anche usare arr.forEach o for (var [i, v] in Iterator (arr)) {} ma nessuno di questi funziona in IE, sebbene tu possa scrivere tu stesso per ogni metodo.
vava

e praticamente ogni libreria ha il proprio metodo anche per questo.
vava

5
Questa risposta è sbagliata La "lunghezza" non sarà inclusa nell'iterazione for-in. Sono incluse solo le proprietà che ti aggiungi.
JacquesB,

16

Non credo di avere molto da aggiungere ad es. La risposta del trittico o la risposta del CMS sul perché l'uso for...indovrebbe essere evitato in alcuni casi.

Tuttavia, vorrei aggiungere che nei browser moderni esiste un'alternativa for...inche può essere utilizzata in quei casi in cui for...innon può essere utilizzata. Tale alternativa è for...of:

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

Nota :

Sfortunatamente, nessuna versione di Internet Explorer supporta for...of( Edge 12+ lo fa), quindi dovrai aspettare un po 'di più prima di poterlo utilizzare nel tuo codice di produzione lato client. Tuttavia, dovrebbe essere sicuro utilizzare nel codice JS sul lato server (se si utilizza Node.js ).


@georgeawg Volevi dire for-ofno for-in?
ᆼ ᆺ ᆼ

15

Il problema con for ... in ...- e questo diventa un problema solo quando un programmatore non capisce davvero la lingua; non è davvero un bug o altro - è che scorre su tutti i membri di un oggetto (beh, tutti i membri enumerabili , ma per ora è un dettaglio). Quando si desidera scorrere solo le proprietà indicizzate di un array, l'unico modo garantito per mantenere le cose semanticamente coerenti è utilizzare un indice intero (ovvero un for (var i = 0; i < array.length; ++i)ciclo di stile).

A qualsiasi oggetto possono essere associate proprietà arbitrarie. Non ci sarebbe nulla di terribile nel caricare proprietà aggiuntive su un'istanza di array, in particolare. Il codice che vuole vedere solo le proprietà indicizzate simili a array deve quindi attenersi a un indice intero. Codice che è pienamente consapevole di ciò che for ... infa e ha davvero bisogno di vedere tutte le proprietà, quindi va bene lo stesso.


Bella spiegazione a punta. Solo curioso. Se avessi un array che si trovava all'interno di un oggetto con proprietà moltiplicate e for in, rispetto a un normale ciclo, tali array verrebbero ripetuti? (quale sarebbe essenzialmente, prestazione lenta, giusto?)
NiCk Newman il

2
@NiCkNewman bene l'oggetto a cui fai riferimento inin un for ... inloop sarà solo
Pointy

Vedo. Solo curioso perché ho oggetti e matrici all'interno del mio oggetto di gioco principale e mi chiedevo se per inning sarebbe stato più doloroso di un semplice ciclo continuo sugli indici.
NiCk Newman,

@NiCkNewman bene il tema di tutta questa domanda è che non dovresti usare for ... insugli array; ci sono molte buone ragioni per non farlo. Non è tanto un problema di prestazioni quanto un problema "assicurati che non si rompa".
Punta il

Bene, i miei oggetti sono archiviati in un array tecnicamente, ecco perché ero preoccupato, qualcosa del tipo: [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]ma sì, ho capito.
NiCk Newman,

9

Inoltre, a causa della semantica, il modo in cui for, intratta gli array (vale a dire lo stesso di qualsiasi altro oggetto JavaScript) non è allineato con altre lingue popolari.

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

9

TL&DR: L' uso del for inloop negli array non è male, anzi il contrario.

Penso che il for inloop sia un gioiello di JS se usato correttamente negli array. Dovresti avere il pieno controllo del tuo software e sapere cosa stai facendo. Vediamo gli svantaggi citati e li confutiamo uno per uno.

  1. Passa anche attraverso le proprietà ereditate: prima di tutto qualsiasi estensione a Array.prototypedovrebbe essere stata fatta usando Object.defineProperty()e il loro enumerabledescrittore dovrebbe essere impostato su false. Qualsiasi libreria che non lo fa non dovrebbe essere utilizzata affatto.
  2. Le proprietà che vengono aggiunte alla catena di ereditarietà vengono successivamente conteggiate: quando si esegue una sottoclasse di array per Object.setPrototypeOfo per classe extend. Si dovrebbe utilizzare ancora Object.defineProperty()che per gruppi predefiniti i writable, enumerablee configurabledescrittori di proprietà a false. Vediamo un esempio di sottoclasse di array qui ...

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

Quindi vedi ... il for inloop ora è sicuro dato che ti importava del tuo codice.

  1. Il for inciclo è lento: l' inferno no. È di gran lunga il metodo di iterazione più veloce se si esegue il loop su array sparsi che sono necessari di volta in volta. Questo è uno dei trucchi prestazionali più importanti che uno dovrebbe sapere. Vediamo un esempio. Passeremo su una matrice sparsa.

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


@Ravi Shanker Reddy Buona impostazione benchmarking. Come ho già detto nella mia risposta, il for inciclo supera gli altri "se" l'array è scarso e più se diventa di dimensioni maggiori. Quindi ho riorganizzato il banco di prova per una matrice sparsa arr, di dimensioni ~ 10000 con solo 50 articoli scelti a caso tra [42,"test",{t:1},null, void 0]indici casuali. Noterai immediatamente la differenza. - >> Dai un'occhiata qui << - .
Redu,

8

Oltre agli altri problemi, la sintassi "for..in" è probabilmente più lenta, poiché l'indice è una stringa, non un numero intero.

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

Probabilmente non importa molto. Gli elementi di matrice sono proprietà di un oggetto basato su matrice o simile a una matrice e tutte le proprietà degli oggetti hanno chiavi di stringa. A meno che il tuo motore JS non lo ottimizzi in qualche modo, anche se utilizzassi un numero, finirà per essere trasformato in una stringa per la ricerca.
cHao,

Indipendentemente da eventuali problemi di prestazioni, se non si conosce JavaScript, utilizzare var i in ae prevedere che l'indice sia un numero intero, quindi fare qualcosa del genere a[i+offset] = <value>metterà i valori in posizioni completamente sbagliate. ("1" + 1 == "11").
szmoore,

8

Un aspetto importante è che for...inscorre solo sulle proprietà contenute in un oggetto con l' attributo della proprietà enumerabile impostato su true. Quindi, se si tenta di iterare su un oggetto usando quindi proprietà arbitrarie potrebbero essere perse se il loro attributo di proprietà enumerabile è falso. È del tutto possibile modificare l'attributo della proprietà enumerabile per gli oggetti Array normali in modo che determinati elementi non vengano enumerati. Sebbene in generale gli attributi della proprietà tendano ad applicarsi alle proprietà della funzione all'interno di un oggetto.for...in

È possibile verificare il valore dell'attributo della proprietà enumerabile di una proprietà:

myobject.propertyIsEnumerable('myproperty')

O per ottenere tutti e quattro gli attributi di proprietà:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Questa è una funzione disponibile in ECMAScript 5: nelle versioni precedenti non era possibile modificare il valore dell'attributo della proprietà enumerabile (era sempre impostato su true).


8

Il for/ infunziona con due tipi di variabili: hashtables (array associativi) e array (non associativi).

JavaScript determinerà automaticamente il modo in cui passa attraverso gli elementi. Quindi, se sai che il tuo array è davvero non associativo, puoi usare for (var i=0; i<=arrayLen; i++)e saltare l'iterazione del rilevamento automatico.

Ma secondo me è meglio usare for/ in, il processo richiesto per quel rilevamento automatico è molto piccolo.

Una vera risposta per questo dipenderà da come i browser analizzano / interpretano il codice JavaScript. Può cambiare tra i browser.

Non riesco a pensare ad altri scopi per non usare 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]);

vero, a meno che non si utilizzino oggetti prototipati. ;) sotto
Ricardo,

Ecco perché Arrayè Objecttroppo
Consulenza gratuita il

2
for ... infunziona con oggetti. Non esiste il rilevamento automatico.
un oliver migliore il

7

Perché ripeterà le proprietà appartenenti agli oggetti nella catena del prototipo se non stai attento.

Puoi usare for.. in, assicurati di controllare ogni proprietà con hasOwnProperty .


2
Non abbastanza: è perfettamente OK aggiungere proprietà denominate arbitrarie alle istanze di array, che verranno testate truedai hasOwnProperty()controlli.
Punta il

Buon punto, grazie. Non sono mai stato abbastanza sciocco da farlo da solo con un array, quindi non l'ho considerato!
JAL

1
@Pointy Non l'ho provato, ma forse questo può essere superato usando un isNaNcontrollo su ogni nome di proprietà.
WynandB,

1
@Wynand idea interessante; tuttavia non vedo davvero perché valga la pena preoccuparsi quando è iterare con un semplice indice numerico è così semplice.
Pointy

@WynandB mi dispiace per il bump, ma sento che una correzione è in ordine: isNaNserve per verificare se una variabile è il valore speciale NaN o no, non può essere usata per controllare "cose ​​diverse dai numeri" (puoi andare con un normale tipo di quello).
Doldt

6

Non è necessariamente negativo (in base a ciò che stai facendo), ma nel caso di array, se è stato aggiunto qualcosa Array.prototype, otterrai risultati strani. Dove ti aspetteresti che questo ciclo venga eseguito tre volte:

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

Se una funzione chiamata helpfulUtilityMethodè stata aggiunta Array's prototype, quindi il ciclo finirebbe per correre quattro volte: keysarebbe 0, 1, 2, e helpfulUtilityMethod. Se ti aspettavi solo numeri interi, oops.


6

Dovresti usare il for(var x in y)solo sugli elenchi di proprietà, non sugli oggetti (come spiegato sopra).


13
Solo una nota su SO - non c'è "sopra" perché i commenti cambiano continuamente l'ordine sulla pagina. Quindi, non sappiamo davvero quale commento intendi. È bene dire "nel commento di x persone" per questo motivo.
JAL

@JAL ... o aggiungi il permalink alla risposta.
WynandB,

5

L'uso del for...inloop per un array non è sbagliato, anche se posso indovinare perché qualcuno ti ha detto che:

1.) Esiste già una funzione o metodo di ordine superiore che ha lo scopo di un array, ma ha più funzionalità e sintassi più snella, chiamata 'forEach': Array.prototype.forEach(function(element, index, array) {} );

2.) Gli array hanno sempre una lunghezza, ma for...ine forEachnon eseguono una funzione per qualsiasi valore che è 'undefined', solo per gli indici che hanno un valore definito. Quindi, se si assegna un solo valore, questi loop eseguiranno una funzione una sola volta, ma poiché un array è elencato, avrà sempre una lunghezza fino all'indice più alto che ha un valore definito, ma quella lunghezza potrebbe passare inosservata quando si usano questi loop.

3.) Lo standard for loop eseguirà una funzione tutte le volte che si definisce nei parametri e poiché un array è numerato, ha più senso definire quante volte si desidera eseguire una funzione. A differenza degli altri loop, il ciclo for può quindi eseguire una funzione per ogni indice dell'array, indipendentemente dal fatto che il valore sia definito o meno.

In sostanza, puoi usare qualsiasi ciclo, ma dovresti ricordare esattamente come funzionano. Comprendi le condizioni in cui reiterano i diversi loop, le loro funzionalità separate e renditi conto che saranno più o meno appropriati per diversi scenari.

Inoltre, può essere considerato una pratica migliore utilizzare il forEachmetodo rispetto al for...inciclo in generale, perché è più facile da scrivere e ha più funzionalità, quindi potresti voler prendere l'abitudine di usare solo questo metodo e standard per, ma il tuo chiamata.

Vedi sotto che i primi due loop eseguono le istruzioni console.log solo una volta, mentre lo standard per loop esegue la funzione tutte le volte che è stato specificato, in questo 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]

4

Ecco i motivi per cui questa (di solito) è una cattiva pratica:

  1. for...ini cicli ripetono tutte le loro proprietà enumerabili e le proprietà enumerabili dei loro prototipi. Di solito in un'iterazione di array vogliamo solo iterare sull'array stesso. E anche se tu stesso potresti non aggiungere nulla all'array, le tue librerie o il tuo framework potrebbero aggiungere qualcosa.

Esempio :

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...ini loop non garantiscono un ordine di iterazione specifico . Anche se al giorno d'oggi si vede l'ordine nella maggior parte dei browser moderni, non esiste ancora alcuna garanzia al 100%.
  2. for...ini loop ignorano gli undefinedelementi dell'array, ovvero gli elementi dell'array che non sono stati ancora assegnati.

Esempio ::

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


2

for ... in è utile quando si lavora su un oggetto in JavaScript, ma non per un array, ma non possiamo ancora dire che sia un modo sbagliato, ma non è raccomandato, guarda questo esempio usando for ... 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, facciamolo con Array ora:

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

Come vedi il risultato lo stesso ...

Ma proviamo qualcosa, prototipiamo qualcosa su Array ...

Array.prototype.someoneelse = "someoneelse";

Ora creiamo un nuovo 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

Vedi qualcuno !!! ... In questo caso eseguiamo il looping del nuovo oggetto Array!

Quindi questo è uno dei motivi per cui dobbiamo usarlo per ... con attenzione, ma non è sempre il caso ...


2

Un ciclo for ... in enumera sempre le chiavi. Le chiavi delle proprietà degli oggetti sono sempre String, anche le proprietà indicizzate di un array:

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


0

anche se non specificamente affrontato da questa domanda, aggiungerei che c'è un ottimo motivo per non usarlo mai per ... in con un NodeList(come si otterrebbe da una querySelectorAllchiamata, in quanto non vede affatto gli elementi restituiti, invece iterando solo sulle proprietà NodeList.

nel caso di un singolo risultato, ho ottenuto:

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

il che spiegava perché il mio for (node in nodes) node.href = newLink;fallimento.

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.