Che cosa significa l'errore JSLint 'body of a for in dovrebbe essere racchiuso in un'istruzione if'?


242

Ho usato JSLint su un mio file JavaScript. Ha gettato l'errore:

for( ind in evtListeners ) {

Problema alla riga 41 carattere 9: il corpo di un for in deve essere racchiuso in un'istruzione if per filtrare le proprietà indesiderate dal prototipo.

Cosa significa questo?


5
Per impostazione predefinita, "in" scorre anche sulle proprietà ereditate. Di solito, il corpo viene inserito if (evtListeners.hasOwnProperty(ind))per limitare l'elaborazione solo alle proprietà proprie (non ereditate). Tuttavia, in alcuni casi si desidera veramente scorrere su tutte le proprietà, comprese quelle ereditate. In tal caso, JSLint ti obbliga a racchiudere il corpo del ciclo in un'istruzione if per decidere quali proprietà desideri veramente. Questo funzionerà e renderà felice JSlint: if (evtListeners[ind] !== undefined)
xorcus,

1
La maggior parte delle risposte sono obsolete. una soluzione aggiornata è disponibile all'indirizzo stackoverflow.com/a/10167931/3138375
eli-bd,

Risposte:


430

Prima di tutto, non usare mai un for inciclo per enumerare un array. Mai. Usa il buon vecchio for(var i = 0; i<arr.length; i++).

Il motivo dietro questo è il seguente: ogni oggetto in JavaScript ha un campo speciale chiamato prototype. Tutto ciò che aggiungi a quel campo sarà accessibile su ogni oggetto di quel tipo. Supponiamo che tu voglia che tutte le matrici abbiano una nuova fantastica funzione chiamata filter_0che filtra gli zero.

Array.prototype.filter_0 = function() {
    var res = [];
    for (var i = 0; i < this.length; i++) {
        if (this[i] != 0) {
            res.push(this[i]);
        }
    }
    return res;
};

console.log([0, 5, 0, 3, 0, 1, 0].filter_0());
//prints [5,3,1]

Questo è un modo standard per estendere oggetti e aggiungere nuovi metodi. Molte biblioteche lo fanno. Tuttavia, diamo un'occhiata a come for infunziona ora:

var listeners = ["a", "b", "c"];
for (o in listeners) {
    console.log(o);
}
//prints:
//  0
//  1
//  2
//  filter_0

Vedi? Improvvisamente pensa che filter_0 sia un altro indice dell'array. Certo, non è in realtà un indice numerico, ma for inenumera attraverso i campi oggetto, non solo gli indici numerici. Quindi ora stiamo enumerando tutti gli indici numerici e filter_0 . Ma filter_0non è un campo di alcun oggetto array particolare, ogni oggetto array ha questa proprietà ora.

Fortunatamente, tutti gli oggetti hanno un hasOwnPropertymetodo, che controlla se questo campo appartiene davvero all'oggetto stesso o se è semplicemente ereditato dalla catena del prototipo e quindi appartiene a tutti gli oggetti di quel tipo.

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}
 //prints:
 //  0
 //  1
 //  2

Si noti che sebbene questo codice funzioni come previsto per gli array, non si dovrebbe mai, mai , usare for ine for each inper gli array. Ricorda che for inenumera i campi di un oggetto, non gli indici o i valori dell'array.

var listeners = ["a", "b", "c"];
listeners.happy = "Happy debugging";

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}

 //prints:
 //  0
 //  1
 //  2
 //  happy

43
Non è necessario utilizzare for inper scorrere su array poiché la lingua non gareggia nell'ordine in cui for inverrà elencato su un array. Potrebbe non essere in ordine numerico. Inoltre, se si usa il costrutto di stile `for (i = 0; i <array.length; i ++), si può essere sicuri di ripetere solo gli indici numerici in ordine e nessuna proprietà alfanumerica.
Breton,

Grazie! Lo salverò come riferimento.
nyuszika7h

Mi sono ritrovato a guardare di nuovo questa risposta perché ero convinto che questo frammento di JSLint fosse rotto. Avevo un codice approssimativamente: for (o negli ascoltatori) {if (hearers.hasOwnProperty (i)) {console.log (o); }} Il problema è che ho avuto un bug, ho cambiato i nomi delle variabili in oe ho perso un riferimento. JSLint è abbastanza intelligente da essere sicuro che stai controllando hasOwnProperty per la proprietà corretta sull'oggetto corretto.
Drewish

12
poiché in, tuttavia, va bene scorrere le proprietà di un oggetto. L'OP non ha mai detto che il for in è stato applicato a un array. HasOwnProperty è la migliore pratica, tuttavia ci sono casi in cui non lo si desidera, ad esempio se un oggetto ne estende un altro e si desidera elencare sia gli oggetti che le proprietà del 'genitore'.
gotofritz,

3
Penso che invece di spaventare le persone lontano dai for-incircuiti (che sono fantastici, a proposito), dovremmo educarli come funzionano (fatto correttamente in questa risposta) e introdurli in Object.defineProperty()modo che possano estendere in modo sicuro i loro prototipi senza rompere nulla. Per inciso, l'estensione dei prototipi di oggetti nativi non dovrebbe essere fatta a meno Object.defineProperty.
Robert Rossmann,

87

Douglas Crockford, l'autore di jslint ha scritto (e parlato) su questo argomento molte volte. C'è una sezione in questa pagina del suo sito Web che tratta questo:

per Dichiarazione

Una classe for di istruzioni dovrebbe avere la forma seguente:

for (initialization; condition; update) {
    statements
}

for (variable in object) {
    if (filter) {
        statements
    } 
}

La prima forma deve essere utilizzata con array e con loop di un numero predeterminabile di iterazioni.

Il secondo modulo deve essere utilizzato con gli oggetti. Tenere presente che i membri aggiunti al prototipo dell'oggetto verranno inclusi nell'enumerazione. È saggio programmare in modo difensivo usando il metodo hasOwnProperty per distinguere i veri membri dell'oggetto:

for (variable in object) {
    if (object.hasOwnProperty(variable)) {
        statements
    } 
}

Crockford ha anche una serie di video sul teatro YUI in cui ne parla. Le serie di video / discorsi di Crockford su javascript sono assolutamente da vedere se si è anche leggermente seri su javascript.


21

Bad: (jsHint genererà un errore)

for (var name in item) {
    console.log(item[name]);
}

Buona:

for (var name in item) {
  if (item.hasOwnProperty(name)) {
    console.log(item[name]);
  }
}

8

La risposta di Vava è nel segno. Se usi jQuery, la $.each()funzione si occupa di questo, quindi è più sicuro da usare.

$.each(evtListeners, function(index, elem) {
    // your code
});

5
Se la performance è una considerazione qui, non consiglierei di usare $.each(o underscore.js _.each) se riesci a cavartela con il forciclo raw . jsperf ha alcuni test comparativi sorprendenti che vale la pena eseguire.
Nick

3
Questo ( jsperf.com/each-vs-each-vs-for-in/3 ) è più realistico perché utilizza il filtro proto di base
dvdrtrgn

7

@all - tutto in JavaScript è un oggetto (), quindi istruzioni come "usa questo solo sugli oggetti" sono un po 'fuorvianti. Inoltre JavaScript non è fortemente digitato in modo che 1 == "1" sia vero (anche se 1 === "1" non lo è, Crockford è grande su questo). Quando si tratta del concetto progressivo di array in JS, la digitazione è importante nella definizione.

@Brenton - Non è necessario essere un dittatore terminologico; "array associativo", "dizionario", "hash", "oggetto", questi concetti di programmazione si applicano tutti a una struttura in JS. Si tratta di coppie di valori nome (chiave, indice), dove il valore può essere qualsiasi altro oggetto (anche le stringhe sono oggetti)

Quindi, new Array()è lo stesso di[]

new Object() è approssimativamente simile a {}

var myarray = [];

Crea una struttura che è un array con la limitazione che tutti gli indici (aka chiavi) devono essere un numero intero. Consente inoltre l'assegnazione automatica di nuovi indici tramite .push ()

var myarray = ["one","two","three"];

È davvero meglio affrontato via for(initialization;condition;update){

Ma per quanto riguarda:

var myarray = [];
myarray[100] = "foo";
myarray.push("bar");

Prova questo:

var myarray = [], i;
myarray[100] = "foo";
myarray.push("bar");
myarray[150] = "baz";
myarray.push("qux");
alert(myarray.length);
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

Forse non il miglior utilizzo di un array, ma solo un'illustrazione che le cose non sono sempre chiare.

Se conosci le tue chiavi e sicuramente se non sono numeri interi, la tua unica opzione di struttura come array è l'oggetto.

var i, myarray= {
   "first":"john",
   "last":"doe",
   100:"foo",
   150:"baz"
};
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

"Usa questo solo sugli oggetti" significa non usarlo su array o qualsiasi altra cosa che estende l'oggetto, altrimenti, come fai notare, sarebbe molto sciocco dal momento che tutto estende l'oggetto
Juan Mendes,

"" array associativo "," dizionario "," hash "," oggetto ", questi concetti di programmazione si applicano tutti a una struttura in JS." No. Sono concetti diversi di lingue diverse, con somiglianze tra loro. Ma se continui a pensare che siano / esattamente gli stessi / e vengano usati allo stesso modo, per gli stessi scopi, ti stai preparando a fare degli errori davvero stupidi che potresti evitare essendo meno ignorante su come la lingua stai usando lavori.
Breton,

2

Sicuramente è un po 'estremo da dire

... non usare mai un ciclo for in per enumerare un array. Mai. Usa good old per (var i = 0; i <arr.length; i ++)

?

Vale la pena evidenziare la sezione dell'estratto di Douglas Crockford

... La seconda forma dovrebbe essere usata con oggetti ...

Se hai bisogno di un array associativo (noto anche come hashtable / dizionario) in cui le chiavi sono nominate anziché indicizzate numericamente, dovrai implementarlo come oggetto, ad es var myAssocArray = {key1: "value1", key2: "value2"...};.

In questo caso myAssocArray.lengthverrà visualizzato null (perché questo oggetto non ha una proprietà 'lunghezza') e il tuo i < myAssocArray.lengthnon ti porterà molto lontano. Oltre a fornire una maggiore praticità, mi aspetto che gli array associativi offrano vantaggi in termini di prestazioni in molte situazioni, poiché le chiavi dell'array possono essere proprietà utili (ad esempio proprietà o nome ID di un membro dell'array), il che significa che non è necessario ripetere un lungo array che valuta ripetutamente se le istruzioni per trovare la voce dell'array che stai cercando.

Comunque, grazie anche per la spiegazione dei messaggi di errore di JSLint, userò ora il controllo 'isOwnProperty' quando interagirò attraverso la mia miriade di array associativi!


1
Sei profondamente confuso. Non esistono "array associativi" in javascript. Questo è rigorosamente un concetto di php.
Breton,

È vero che questi oggetti non hanno una lengthproprietà, ma puoi farlo in un altro modo:var myArr = []; myArr['key1'] = 'hello'; myArr['key2'] = 'world';
nyuszika7h

3
@ Nyuszika7H Questo è il modo sbagliato. Se non hai bisogno dell'array indicizzato intero, non dovresti usarlo var myArr = [], dovrebbe essere var myArr = {}in PHP che sono la stessa cosa, ma non in JS.
Juan Mendes,

La "matrice" associativa non è una matrice.
Vincent McNabb,


0

Solo per aggiungere all'argomento for in / for / $. Ciascuno, ho aggiunto un caso di test jsperf per l'utilizzo di $ .each vs for in: http://jsperf.com/each-vs-for-in/2

Diversi browser / versioni lo gestiscono in modo diverso, ma sembra $ .each e subito in in sono le opzioni più economiche dal punto di vista delle prestazioni.

Se stai usando for in per scorrere attraverso un array / oggetto associativo, sapendo cosa stai cercando e ignorando tutto il resto, usa $ .each se usi jQuery o semplicemente per in (e poi una pausa; una volta che hai raggiunto quello che sai dovrebbe essere l'ultimo elemento)

Se stai iterando attraverso un array per eseguire qualcosa con ogni coppia di chiavi in ​​esso, dovresti usare il metodo hasOwnProperty se NON usi jQuery e usa $ .each se usi jQuery.

Usa sempre for(i=0;i<o.length;i++)se non hai bisogno di un array associativo però ... lol chrome ha eseguito il 97% più velocemente di un for in o$.each

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.