javascript - length - jquery for loop




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

Poiché gli elementi JavaScript vengono salvati come proprietà dell'oggetto standard, non è consigliabile iterare attraverso gli array JavaScript utilizzando for ... nei cicli perché verranno elencati gli elementi normali e tutte le proprietà enumerabili.

Da https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections

Mi è stato detto di non usare for...in con gli array in JavaScript. Perchè no?


A parte il fatto che for ... in loop su tutte le proprietà enumerabili (che non è uguale a "tutti gli elementi dell'array"!), Consultare 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 è specificato ...

(Enfasi mia.)

Ciò significa che se un browser lo desidera, potrebbe passare attraverso le proprietà nell'ordine in cui sono state inserite. O in ordine numerico. O in ordine lessicale (dove "30" viene prima di "4"! Tenete a mente tutte le chiavi degli oggetti - e quindi, tutti gli indici di array - sono in realtà stringhe, quindi ha senso). Poteva passare attraverso di loro per bucket, se implementava oggetti come tabelle hash. O prendi qualcosa di simile e aggiungi "indietro". Un browser potrebbe persino scorrere casualmente ed essere conforme all'ECMA-262, purché abbia visitato ciascuna proprietà esattamente una volta.

In pratica, la maggior parte dei browser attualmente è in grado di scorrere più o meno nello stesso ordine. Ma non c'è niente che dice che devono. Questo è specifico per l'implementazione e potrebbe cambiare in qualsiasi momento se si scoprisse che un altro modo è molto più efficiente.

In ogni caso, for ... porta con sé nessuna connotazione di ordine. Se ti interessa dell'ordine, sii esplicito a riguardo e usa un ciclo for regolare con un indice.


Ci sono tre ragioni per cui non si dovrebbe usare for..in per scorrere gli elementi dell'array:

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

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

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


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 JavaScript librerie JavaScript potrebbero fare cose come questa, che influenzeranno qualsiasi array che creerai:

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


In isolamento, non c'è nulla di sbagliato nell'usare for-in sugli array. For-in esegue iterazioni sui nomi di proprietà di un oggetto e, nel caso di un array "out-of-the-box", le proprietà corrispondono agli indici di array. (I propers built come length , toString e così via non sono inclusi nell'iterazione.)

Tuttavia, se il codice (o il framework che si sta utilizzando) aggiunge proprietà personalizzate agli array o al prototipo dell'array, queste 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 tranquillamente usare for-in.

Se sei in dubbio, probabilmente non dovresti usare 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 definisci arr come:

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

Quindi la matrice ha due elementi, ma una lunghezza di 101. L'utilizzo di for-in produrrà due indici, mentre il ciclo for darà 101 indici, in cui il 99 ha un valore undefined .


Inoltre, a causa della semantica, la via for, in array di treats (cioè uguale a qualsiasi altro oggetto JavaScript) non è allineata con altri linguaggi 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"

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

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

2.) Le matrici hanno sempre una lunghezza, ma for...in e forEach non eseguono una funzione per qualsiasi valore che è 'undefined' , solo per gli indici che hanno un valore definito. Quindi se assegni un solo valore, questi cicli eseguiranno una funzione solo una volta, ma poiché un array è enumerato, 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 tante volte quante definite nei parametri, e poiché una matrice è numerata, 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 loop, ma dovresti ricordare esattamente come funzionano. Comprendere le condizioni sulle quali i vari cicli ripetono, le loro funzionalità separate e rendersi conto che saranno più o meno appropriati per i diversi scenari.

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

Vedi sotto che i primi due loop eseguono solo le istruzioni console.log una volta, mentre lo standard for loop esegue la funzione tante volte quante specificate, 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]

Non è necessariamente negativo (basato su quello che stai facendo), ma nel caso degli array, se qualcosa è stato aggiunto a 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 al prototype di Array , il ciclo verrebbe eseguito quattro volte: la key sarebbe 0 , 1 , 2 e utile helpfulUtilityMethod . Se ti aspettavi solo numeri interi, oops.


Oltre agli altri problemi, la sintassi "for..in" è probabilmente più lenta, perché l'indice è una stringa, non un 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'

Oltre alle ragioni fornite in altre risposte, potresti non voler usare la struttura "for ... in" se devi fare matematica con la variabile contatore 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.


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

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


Perché per ... enumera attraverso l'oggetto che contiene l'array, non l'array stesso. Se aggiungo una funzione alla catena di prototipi degli array, anche questa 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 = foo
1 = bar
myOwnFunction = function() { alert(this); }

E poiché non si può mai essere sicuri che nulla verrà aggiunto alla catena del prototipo, 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 = foo
1 = bar

Risposta breve: non ne vale la pena.

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

Risposta lunga: non ne vale la pena, per ragioni che seguono:

  • L'uso for (var i in array) {} farà in modo che l''array' venga interpretato come qualsiasi altro oggetto puro , attraversando la catena di proprietà dell'oggetto e, in ultima analisi, si esegue più lentamente di un loop for indici.
  • Non è garantito restituire le proprietà dell'oggetto in ordine sequenziale come ci si potrebbe aspettare.
  • L'uso di hasOwnProperty() o isNaN() controlla per filtrare le proprietà dell'oggetto è un sovraccarico aggiuntivo che lo fa eseguire (anche più) più lentamente. Inoltre, l'introduzione di tale logica aggiuntiva annulla il motivo principale per l'utilizzo in primo luogo, vale a dire a causa del formato più conciso.

Per questi motivi non esiste nemmeno un compromesso accettabile tra prestazioni e convenienza. In realtà, non c'è alcun beneficio a meno che l'intento sia quello di trattare l'array come un oggetto puro ed eseguire operazioni sulle proprietà dell'oggetto dell'array.


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


for / in funziona con due tipi di variabili: hashtables (array associativi) e array (non-associative).

JavaScript determinerà automaticamente il modo in cui passa attraverso gli oggetti. 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 a mio parere, è meglio usare for / in , il processo richiesto per quel rilevamento automatico è molto piccolo.

Una vera risposta dipenderà da come il browser analizza / interpreta 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]);

TL & DR: Usare il ciclo for in non è male, anzi è il contrario.

Penso che il ciclo for in è una gemma di JS se usato correttamente negli array. Ci si aspetta che tu abbia il pieno controllo del tuo software e sappia cosa stai facendo. Vediamo gli inconvenienti citati e li confutiamo uno per uno.

  1. Passa anche attraverso le proprietà ereditate: prima di tutto qualsiasi estensione al tipo Array.prototype avrebbe dovuto essere eseguita usando Object.defineProperty() e il loro descrittore enumerable dovrebbe essere impostato su false . Qualsiasi libreria che non lo fa non dovrebbe essere utilizzata affatto.
  2. Le proprietà che si aggiungono alla catena di ereditarietà vengono successivamente conteggiate: quando si esegue la sottoclassificazione dell'array per Object.setPrototypeOf o per extend Classe. Dovresti nuovamente usare Object.defineProperty() che per impostazione predefinita imposta i descrittori di proprietà writable , enumerable e configurable su false . Vediamo un esempio di sottoclasse dell'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 ... for in ciclo continuo è ora sicuro poiché ti importa del tuo codice.

  1. Il ciclo for in è lento: Hell no. È il metodo di gran lunga più veloce di iterazione se si esegue il ciclo su array sparsi che sono necessari di volta in volta. Questo è uno dei trucchi di prestazioni più importanti che si dovrebbero sapere. Vediamo un esempio. Effettueremo un ciclo su un array sparse.

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


A per ... in loop enumera sempre i tasti. Le chiavi delle proprietà degli oggetti sono sempre String, anche le proprietà indicizzate di una matrice:

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




iteration