JavaScript - Nuances de myArray.forEach vs boucle for

88

J'ai vu beaucoup de questions suggérant d'utiliser:

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

au lieu de:

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

pour les tableaux, en raison d'une itération incohérente ( voir ici ).


Cependant, je n'arrive pas à trouver quoi que ce soit qui semble préférer la boucle orientée objet:

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

Ce qui me semble beaucoup plus intuitif.

Pour mon projet actuel, la compatibilité IE8 est importante, et j'envisage d'utiliser le polyfill de Mozilla , mais je ne suis pas sûr à 100% de la manière dont cela fonctionnera.

  • Existe-t-il des différences entre la boucle for standard (le premier exemple ci-dessus) et l'implémentation Array.prototype.forEach par les navigateurs modernes?
  • Y a-t-il une différence entre les implémentations de navigateur modernes et l'implémentation de Mozilla liée à ci-dessus (avec une attention particulière à IE8)?
  • Les performances ne sont pas autant un problème, mais simplement la cohérence avec laquelle les propriétés sont itérées.
Michael Lewis
la source
6
Il n'est pas possible de breaksortir forEach. Mais un gros avantage est de créer une nouvelle portée avec la fonction. Avec le polyfill, vous ne devriez pas avoir de problèmes (du moins je n'en ai rencontré aucun).
hgoebl
Les problèmes que vous pouvez avoir avec les anciens IE ne sont pas le shim lui-même, mais le constructeur de tableau cassé / littéral écrit holesundefineddevrait être et d'autres méthodes cassées, comme sliceet hasOwnPropertyécrites à des objets DOM de type arraylike. Mes tests et es5 shimont montré de telles méthodes calées conformes à la spécification (non testé la cale de MDN).
Xotic750
1
Et pour sortir d'une forboucle, c'est à ça que ça sert some.
Xotic750
1
"Cependant, je n'arrive pas à trouver quoi que ce soit qui semble préférer la boucle orientée objet:" Je préfère l'appeler de manière fonctionnelle vs impérative.
Memke
Vous pouvez utiliser Array.find()pour sortir de la boucle après avoir trouvé une première correspondance.
Mottie

Réponses:

121

La différence la plus importante entre la forboucle et la forEachméthode est que, avec la première, vous pouvez breaksortir de la boucle. Vous pouvez simuler continueen retournant simplement de la fonction passée à forEach, mais il n'y a aucun moyen d'arrêter complètement la boucle.

En dehors de cela, les deux accomplissent effectivement la même fonctionnalité. Une autre différence mineure concerne la portée de l'index (et toutes les variables contenant) dans la boucle for, en raison du levage des variables.

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

Cependant, je trouve que forEachc'est beaucoup plus expressif - cela représente votre intention de parcourir chaque élément d'un tableau, et cela vous fournit une référence à l'élément, pas seulement à l'index. Dans l'ensemble, cela dépend principalement de vos goûts personnels, mais si vous pouvez l'utiliser forEach, je vous recommanderais de l'utiliser.


Il existe quelques différences plus substantielles entre les deux versions, notamment en ce qui concerne les performances. En fait, la boucle for simple fonctionne considérablement mieux que la forEachméthode, comme le démontre ce test jsperf .

C'est à vous de décider si une telle performance est nécessaire ou non, et dans la plupart des cas, je préférerais l'expressivité à la vitesse. Cette différence de vitesse est probablement due aux différences sémantiques mineures entre la boucle de base et la méthode lors du fonctionnement sur des tableaux clairsemés, comme indiqué dans cette réponse .

Si vous n'avez pas besoin du comportement de forEach et / ou que vous avez besoin de sortir de la boucle tôt, vous pouvez utiliser Lo-Dash _.eachcomme alternative, qui fonctionnera également avec plusieurs navigateurs. Si vous utilisez jQuery, il fournit également un similaire $.each, notez simplement les différences dans les arguments passés à la fonction de rappel dans chaque variante.

(Quant au forEachpolyfill, il devrait fonctionner sans problème dans les anciens navigateurs, si vous choisissez d'emprunter cette voie.)

Alexis King
la source
1
Il est à noter que les versions de la bibliothèque eachne se comportent pas de la même manière que la spécification ECMA5 de forEach, elles ont tendance à traiter tous les tableaux comme denses (pour éviter les bogues IE, à condition que vous en soyez conscient). Peut être un "gotcha" autrement. En référence github.com/es-shims/es5-shim/issues/190
Xotic750
7
Il existe également des méthodes de prototype qui vous permettent de casser, par exemple, Array.prototype.somequi bouclera jusqu'à ce que vous retourniez une valeur de vérité ou jusqu'à ce qu'elle ait complètement bouclé à travers le tableau. Array.prototype.everyest similaire à Array.prototype.somemais s'arrête si vous renvoyez une valeur erronée.
axelduch
Si vous voulez voir des résultats de test sur différents navigateurs et ces shims, vous pouvez jeter un oeil ici, ci.testling.com/Xotic750/util-x
Xotic750
De plus, utiliser Array.prototype.forEach mettrait votre code à la merci de l'extension. Par exemple, si vous écrivez un code à intégrer dans une page, il est possible que Array.prototype.forEach soit écrasé par autre chose.
Marble Daemon
2
@MarbleDaemon La chance de cela est si minuscule que c'est effectivement impossible et donc négligeable. ÉcraserArray.prototype.forEach avec une version non compatible aurait le potentiel de briser tellement de bibliothèques que l'éviter pour cette raison ne serait d'aucune façon utile.
Alexis King
12

Vous pouvez utiliser votre fonction foreach personnalisée qui fonctionnera beaucoup mieux que Array.

Vous devez l'ajouter une fois à votre code. Cela ajoutera une nouvelle fonction au tableau.

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

Ensuite, vous pouvez l'utiliser n'importe où, comme le tableau.

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

});

La seule différence c'est 3 fois plus rapide. https://jsperf.com/native-arr-foreach-vs-custom-foreach

MISE À JOUR: Dans la nouvelle version de Chrome, les performances de .forEach () ont été améliorées. Cependant, la solution peut offrir des performances supplémentaires dans d'autres navigateurs.

JSPerf

Mouette
la source
4

Il est suggéré par de nombreux développeurs (par exemple Kyle Simpson) de l'utiliser .forEachpour indiquer que le tableau aura un effet secondaire et .mappour des fonctions pures. forles boucles conviennent bien comme une solution à usage général pour un nombre connu de boucles ou tout autre cas qui ne convient pas car il est plus facile de communiquer en raison de son large support dans la majorité des langages de programmation.

par exemple

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

En ce qui concerne la convention, cela semble le meilleur, mais la communauté n'a pas vraiment décidé d'une convention sur les nouvelles for inboucles de protocole d'itération . En général, je pense que c'est une bonne idée de suivre les concepts FP que la communauté JS semble être ouverte à adopter.

steviejay
la source