JavaScript - Nuancen von myArray.forEach vs for-Schleife

88

Ich habe viele Fragen gesehen, die vorschlagen, Folgendes zu verwenden:

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

anstatt:

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

für Arrays aufgrund inkonsistenter Iteration ( siehe hier ).


Ich kann jedoch anscheinend nichts finden, was die objektorientierte Schleife zu bevorzugen scheint:

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

Was mir viel intuitiver erscheint.

Für mein aktuelles Projekt ist die IE8-Kompatibilität wichtig, und ich denke darüber nach, Mozillas Polyfill zu verwenden . Ich bin mir jedoch nicht 100% sicher, wie dies funktionieren wird.

  • Gibt es Unterschiede zwischen dem Standard für die Schleife (das erste Beispiel oben) und der Implementierung von Array.prototype.forEach durch moderne Browser?
  • Gibt es einen Unterschied zwischen modernen Browser-Implementierungen und der oben genannten Implementierung von Mozilla (unter besonderer Berücksichtigung von IE8)?
  • Die Leistung ist weniger ein Problem, sondern nur die Konsistenz, mit der die Eigenschaften wiederholt werden.
Michael Lewis
quelle
6
Es ist nicht möglich, breakrauszukommen forEach. Ein großer Vorteil ist jedoch die Schaffung eines neuen Bereichs mit der Funktion. Mit der Polyfüllung sollten Sie keine Probleme haben (zumindest bin ich auf keine gestoßen).
Hgoebl
Die Probleme, die Sie mit älteren IE haben können, sind nicht das Shim selbst, sondern der kaputte Array-Konstruktor / Literal, holeswo es sein undefinedsollte, und andere kaputte Methoden, wie sliceund hasOwnPropertywrt zu arraylike DOM-Objekten. Meine Tests es5 shimhaben gezeigt, dass solche Shimmed-Methoden der Spezifikation entsprechen (nicht getestete MDN-Shim).
Xotic750
1
Und um aus einer forSchleife auszubrechen , ist das das, wofür some.
Xotic750
1
"Ich kann jedoch anscheinend nichts finden, was die objektorientierte Schleife zu bevorzugen scheint:" Ich würde es eher als funktionalen Weg als als Imperativ bezeichnen.
Memke
Sie können verwenden Array.find(), um aus der Schleife auszubrechen, nachdem Sie eine erste Übereinstimmung gefunden haben.
Mottie

Antworten:

121

Der wesentlichste Unterschied zwischen der forSchleife und der forEachMethode besteht darin, dass Sie mit der ersteren möglicherweise breakaus der Schleife herauskommen. Sie können simulieren, continueindem Sie einfach von der an übergebenen Funktion zurückkehren. Es forEachgibt jedoch keine Möglichkeit, die Schleife insgesamt zu beenden.

Abgesehen davon erreichen die beiden effektiv die gleiche Funktionalität. Ein weiterer kleiner Unterschied betrifft den Umfang des Index (und aller enthaltenen Variablen) in der for-Schleife aufgrund des Hebens von Variablen.

// '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) { ... });

Ich finde das forEachjedoch viel ausdrucksvoller - es repräsentiert Ihre Absicht, jedes Element eines Arrays zu durchlaufen, und bietet Ihnen einen Verweis auf das Element, nicht nur auf den Index. Insgesamt kommt es hauptsächlich auf den persönlichen Geschmack an, aber wenn Sie es verwenden können forEach, würde ich empfehlen, es zu verwenden.


Es gibt einige wesentliche Unterschiede zwischen den beiden Versionen, insbesondere in Bezug auf die Leistung. Tatsächlich ist die einfache for-Schleife erheblich besser als die forEachMethode, wie dieser jsperf-Test zeigt .

Ob eine solche Leistung für Sie erforderlich ist oder nicht, liegt bei Ihnen. In den meisten Fällen würde ich Ausdruckskraft gegenüber Geschwindigkeit bevorzugen. Dieser Geschwindigkeitsunterschied ist wahrscheinlich auf die geringfügigen semantischen Unterschiede zwischen der Basisschleife und der Methode zurückzuführen, wenn mit spärlichen Arrays gearbeitet wird, wie in dieser Antwort angegeben .

Wenn Sie das Verhalten von forEachund / oder das frühzeitige Ausbrechen aus der Schleife nicht benötigen , können Sie _.eachalternativ Lo-Dash's verwenden , die auch browserübergreifend funktionieren. Wenn Sie jQuery verwenden, bietet es auch eine ähnliche Funktion $.each. Beachten Sie lediglich die Unterschiede in den Argumenten, die in jeder Variante an die Rückruffunktion übergeben werden.

(Die forEachPolyfüllung sollte in älteren Browsern problemlos funktionieren, wenn Sie diesen Weg wählen.)

Alexis King
quelle
1
Es ist anzumerken, dass sich die Bibliotheksversionen eachnicht wie die ECMA5-Spezifikation von verhalten forEach, sondern dazu neigen, alle Arrays als dicht zu behandeln (um IE-Fehler zu vermeiden, sofern Sie dies wissen). Kann sonst ein "Gotcha" sein. Als Referenz github.com/es-shims/es5-shim/issues/190
Xotic750
7
Es gibt auch einige Prototypmethoden, mit denen Sie eine Unterbrechung durchführen können, z. B. Array.prototype.somewelche Schleife, bis Sie einen wahrheitsgemäßen Wert zurückgeben oder bis sie das Array vollständig durchlaufen hat. Array.prototype.everyist ähnlich, Array.prototype.somestoppt jedoch, wenn Sie einen falschen Wert zurückgeben.
Axelduch
Wenn Sie einige Testergebnisse in verschiedenen Browsern und solchen Shims sehen möchten
Xotic750
Wenn Sie Array.prototype.forEach verwenden, wird Ihr Code der Erweiterung ausgeliefert. Wenn Sie beispielsweise einen Code schreiben, der in eine Seite eingebettet werden soll, besteht die Möglichkeit, dass Array.prototype.forEach mit etwas anderem überschrieben wird.
Marble Daemon
2
@MarbleDaemon Die Chance dafür ist so gering, dass es praktisch unmöglich und daher vernachlässigbar ist. Das Überschreiben Array.prototype.forEachmit einer nicht kompatiblen Version hätte das Potenzial, so viele Bibliotheken zu beschädigen, dass es sowieso nicht helfen würde, es aus diesem Grund zu vermeiden.
Alexis King
12

Sie können Ihre benutzerdefinierte foreach-Funktion verwenden, die eine viel bessere Leistung als Array.forEach bietet

Sie sollten dies einmal zu Ihrem Code hinzufügen. Dadurch wird dem Array eine neue Funktion hinzugefügt.

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

Dann können Sie es überall wie Array.forEach verwenden

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

});

Der einzige Unterschied ist, dass es dreimal schneller ist. https://jsperf.com/native-arr-foreach-vs-custom-foreach

UPDATE: In der neuen Chrome-Version wurde die Leistung von .forEach () verbessert. Die Lösung kann jedoch die zusätzliche Leistung in anderen Browsern bieten.

JSPerf

Möwe
quelle
4

Es wird von vielen Entwicklern (z. B. Kyle Simpson) empfohlen, zu verwenden, .forEachum anzuzeigen, dass das Array einen Nebeneffekt hat, und .mapfür reine Funktionen. forSchleifen eignen sich gut als Allzwecklösung für die bekannte Anzahl von Schleifen oder für jeden anderen Fall, der nicht passt, da die Kommunikation aufgrund der breiten Unterstützung für die meisten Programmiersprachen einfacher ist.

z.B

/* 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()));

Konventionell scheint dies am besten zu sein, jedoch hat sich die Community nicht wirklich auf eine Konvention für die neueren Iterationsprotokollschleifen festgelegt for in. Generell halte ich es für eine gute Idee, den FP-Konzepten zu folgen, für deren Übernahme die JS-Community offenbar offen ist.

steviejay
quelle