JavaScript - Sfumature di myArray.forEach rispetto al ciclo for


88

Ho visto molte domande che suggeriscono di utilizzare:

for (var i = 0; i < myArray.length; i++){ /* ... */ }

invece di:

for (var i in myArray){ /* ... */ }

per gli array, a causa di iterazioni incoerenti ( vedere qui ).


Tuttavia, non riesco a trovare nulla che sembri preferire il ciclo orientato agli oggetti:

myArray.forEach(function(item, index){ /* ... */ });

Il che mi sembra molto più intuitivo.

Per il mio progetto attuale, la compatibilità con IE8 è importante e sto valutando di utilizzare il polyfill di Mozilla , tuttavia non sono sicuro al 100% come funzionerà.

  • Esistono differenze tra lo standard for loop (il primo esempio sopra) e l'implementazione Array.prototype.forEach dai browser moderni?
  • C'è qualche differenza tra le moderne implementazioni del browser e l'implementazione di Mozilla collegata a sopra (con particolare riguardo a IE8)?
  • Le prestazioni non sono tanto un problema, ma solo la coerenza con cui le proprietà vengono iterate.

6
Non è possibile breakuscire da forEach. Ma un grande vantaggio è creare un nuovo ambito con la funzione. Con il polyfill non dovresti avere problemi (almeno io non ne ho riscontrati).
hgoebl

I problemi che puoi avere con il vecchio IE, non sono lo shim stesso, ma il costruttore di array rotto / valore letterale holesdove undefineddovrebbe essere e altri metodi non funzionanti, come slicee hasOwnPropertyrispetto a oggetti DOM di tipo array. I miei test e es5 shimho mostrato che tali metodi con shim sono conformi alle specifiche (non testato con lo shim di MDN).
Xotic750

1
E rispetto alla rottura di un forciclo, questo è ciò che someserve.
Xotic750

1
"Tuttavia, non riesco a trovare nulla che sembri preferire il ciclo orientato agli oggetti:" Lo chiamerei piuttosto modo funzionale vs imperativo.
Memke

Puoi usarlo Array.find()per uscire dal loop dopo aver trovato una prima corrispondenza.
Mottie

Risposte:


122

La differenza più sostanziale tra il forciclo e il forEachmetodo è che, con il primo, potresti breakuscire dal giro. Puoi simulare continuesemplicemente tornando dalla funzione passata aforEach , ma non c'è modo di interrompere del tutto il ciclo.

A parte questo, i due realizzano effettivamente la stessa funzionalità. Un'altra piccola differenza riguarda l'ambito dell'indice (e tutte le variabili contenenti) nel ciclo for, a causa del sollevamento delle variabili.

// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }

// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });

Tuttavia, trovo che forEachsia molto più espressivo: rappresenta il tuo intento di iterare attraverso ogni elemento di un array e ti fornisce un riferimento all'elemento, non solo all'indice. Nel complesso, dipende principalmente dai gusti personali, ma se puoi usarlo forEach, ti consiglio di usarlo.


Ci sono alcune differenze più sostanziali tra le due versioni, in particolare per quanto riguarda le prestazioni. In effetti, il semplice ciclo for funziona notevolmente meglio del forEachmetodo, come dimostrato da questo test jsperf .

Spetta a te decidere se tale esecuzione sia necessaria o meno e nella maggior parte dei casi preferirei l'espressività alla velocità. Questa differenza di velocità è probabilmente dovuta alle minori differenze semantiche tra il ciclo di base e il metodo quando si opera su array sparsi, come indicato in questa risposta .

Se non hai bisogno del comportamento di forEache / o hai bisogno di uscire presto dal ciclo, puoi usare Lo-Dash _.eachcome alternativa, che funzionerà anche cross-browser. Se stai usando jQuery, fornisce anche un file simile$.each , basta notare le differenze negli argomenti passati alla funzione di callback in ogni variazione.

(Per quanto riguarda il forEachpolyfill, dovrebbe funzionare senza problemi nei browser meno recenti, se scegli di seguire quella strada.)


1
Vale la pena notare che le versioni della libreria eachnon si comportano allo stesso modo della specifica ECMA5 di forEach, tendono a trattare tutti gli array come densi (per evitare bug di IE, a condizione che tu sia a conoscenza). Può essere un "trucchetto" altrimenti. Come riferimento github.com/es-shims/es5-shim/issues/190
Xotic750

7
Inoltre ci sono alcuni metodi prototipo che ti permettono di rompere come Array.prototype.someche verrà ripetuto fino a quando non restituirai un valore vero o fino a quando non avrà eseguito il ciclo completamente attraverso l'array. Array.prototype.everyè simile a Array.prototype.somema si interrompe se restituisci un valore falso.
axelduch

Se vuoi vedere alcuni risultati di test su browser diversi e tali spessori, puoi dare un'occhiata qui, ci.testling.com/Xotic750/util-x
Xotic750

Inoltre, l'utilizzo di Array.prototype.forEach metterebbe il tuo codice in balia dell'estensione. Ad esempio, se stai scrivendo un codice da incorporare in una pagina, c'è la possibilità che Array.prototype.forEach venga sovrascritto con qualcos'altro.
Marble Daemon

2
@MarbleDaemon La possibilità di ciò è così minuscola che è effettivamente impossibile e quindi trascurabile. La sovrascrittura Array.prototype.forEachcon qualche versione non compatibile avrebbe il potenziale di rompere così tante librerie che evitarla per quel motivo non sarebbe d'aiuto, comunque.
Alexis King

12

Puoi usare la tua funzione foreach personalizzata che funzionerà molto meglio di Array.forEach

Dovresti aggiungerlo una volta al tuo codice. Questo aggiungerà una nuova funzione all'array.

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for(var i=0; i<len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

Quindi puoi usarlo ovunque come Array.forEach

[1,2,3].customForEach(function(val, i){

});

L'unica differenza è 3 volte più veloce. https://jsperf.com/native-arr-foreach-vs-custom-foreach

AGGIORNAMENTO: nella nuova versione di Chrome le prestazioni di .forEach () sono state migliorate. Tuttavia, la soluzione può fornire prestazioni aggiuntive in altri browser.

JSPerf


4

È suggerito da molti sviluppatori (ad esempio Kyle Simpson) .forEachper indicare che l'array avrà un effetto collaterale e .mapper funzioni pure.fori loop si adattano bene come soluzione generica per un numero noto di loop o qualsiasi altro caso che non si adatta in quanto è più facile comunicare a causa del suo ampio supporto nella maggior parte dei linguaggi di programmazione.

per esempio

/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
  //Do Something
}

/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));

/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.push(el.toUpperCase()));

Per quanto riguarda la convenzione, questa sembra la migliore, tuttavia la comunità non ha davvero stabilito una convenzione sui for incicli di protocollo di iterazione più recenti . In generale, penso che sia una buona idea seguire i concetti di FP che la comunità JS sembra essere aperta ad adottare.

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.