JavaScript pour… en vs pour

461

Pensez-vous qu'il y a une grande différence entre les boucles ... et les boucles? Quel type de "pour" préférez-vous utiliser et pourquoi?

Disons que nous avons un tableau de tableaux associatifs:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Nous pouvons donc itérer:

for (var i = 0; i < myArray.length; i++)

Et:

for (var i in myArray)

Je ne vois pas de grande différence. Y a-t-il des problèmes de performances?

andrii
la source
13
Notez que nous avons également, à partir de JS 1.6 , a: myArray.forEach(callback[, thisarg]).
Benji XVI
14
@Benji array.forEach est en fait dans ES5.
mikemaccana
2
dans une boucle for-in, vous avez besoin d'un conditionnel qui ressemble à ceci:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky
6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); est OK à utiliser à peu près partout sauf dans IE8 - et c'est de loin la syntaxe la plus élégante
Jon z
5
Il y a également une for...ofdéclaration dans ECMAScript 6 , par exemple:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Réponses:

548

Le choix doit être basé sur l'idiome le mieux compris.

Un tableau est itéré en utilisant:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

Un objet utilisé comme tableau associatif est itéré en utilisant:

for (var key in o)
  //do stuff with o[key]

Sauf si vous avez des raisons bouleversantes, respectez le modèle d'utilisation établi.

AnthonyWJones
la source
38
Il convient de mentionner que c'est une bonne pratique à utiliser pour ... avec le filtrage de l'instruction if. Il existe une méthode pratique de l'objet "obj.hasOwnProperty (membre)" qui vérifie si un membre renvoyé par l'itérateur est réellement membre de l'objet. Voir: javascript.crockford.com/code.html
Damir Zekić
57
Comme indiqué dans une ou plusieurs autres réponses, le "pour ... dans" ne fonctionne pas correctement pour les tableaux, car il itère sur toutes les propriétés et méthodes des tableaux. Ainsi, vous ne devez utiliser "for ... in" que pour itérer sur les propriétés des objets. Sinon, respectez "pour (i = 0; i <quelque chose; i ++)"
Denilson Sá Maia
Pour des raisons de performances, IMO, il est préférable d'évaluer la longueur du tableau avant le for, et non pas une longueur à chaque fois dans la boucle.
UpTheCreek
9
@UpTheCreek: C'est certainement vrai lorsque le tableau est en fait quelque chose renvoyé par le HTMLDOM, cependant, je me demande quelle taille un tableau javascript standard devrait avoir avant que vous puissiez voir une différence appréciable? Personnellement, je garderais le code aussi simple que possible jusqu'à ce qu'il soit nécessaire de faire quelque chose de différent.
AnthonyWJones
2
@Pichan Je pense que tu veux dire i < l, non i < a, dans ta condition de boucle.
Max Nanasy
161

Douglas Crockford recommande dans JavaScript: The Good Parts (page 24) pour éviter d'utiliser l' for inénoncé.

Si vous utilisez for inpour parcourir les noms de propriété dans un objet, les résultats ne sont pas classés. Pire: vous pourriez obtenir des résultats inattendus; il inclut les membres hérités de la chaîne de prototypes et le nom des méthodes.

Tout sauf les propriétés peut être filtré avec .hasOwnProperty. Cet exemple de code fait ce que vous vouliez probablement à l'origine:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}
Benno Richters
la source
70
for ... in est parfaitement approprié pour boucler sur les propriétés d'un objet. Il n'est pas approprié pour boucler sur des éléments de tableau. Si vous ne pouvez pas comprendre la différence entre ces scénarios, alors oui, vous devez éviter pour ... dans; sinon, devenez fou.
Shog9
8
Vous voulez souligner le fait qu'il n'est PAS COMMANDÉ! Cela pourrait être un gros problème et un bug difficile à attraper.
Jason
4
+1 pour "il inclut les membres hérités de la chaîne de prototypes et le nom des méthodes". Vous vous amuserez si quelqu'un utilise votre code avec le prototype chargé (même si votre code ne l'utilise pas réellement), par exemple.
ijw
13
N'oubliez pas de déclarer la namevariable: for(var name in object)...sinon, si ce code est dans une fonction par exemple, la namevariable finit par être une propriété de l'objet global (une affectation à un identifiant non déclaré fait cela), également dans le nouvel ECMAScript 5 Mode strict, ce code lancera un ReferenceError.
CMS
6
@Nosredna: Il y a un problème concernant l'ordre d'itération pour Chrome, déposé par nul autre que John Resig, qui est marqué comme WontFix. code.google.com/p/chromium/issues/detail?id=883 . Même avant Chrome, l'ordre d'itération n'était pas le même pour tous les navigateurs si vous supprimez puis ajoutez à nouveau une propriété. IE 9 se comporte également comme le chrome (soi-disant pour des améliorations de vitesse). Alors ... Arrêtez de diffuser des informations inexactes, vous seriez très naïf de continuer à en dépendre.
Juan Mendes
62

FYI - Utilisateurs jQuery


La each(callback)méthode de jQuery utilise la for( ; ; )boucle par défaut, et n'utilisera for( in ) que si la longueur est undefined.

Par conséquent, je dirais qu'il est prudent de supposer le bon ordre lors de l'utilisation de cette fonction.

Exemple :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

L'inconvénient de cette utilisation est que si vous utilisez une logique non UI, vos fonctions seront moins portables sur d'autres frameworks. La each()fonction est probablement mieux réservée pour une utilisation avec les sélecteurs jQuery et for( ; ; )pourrait être recommandée dans le cas contraire.


Jason
la source
4
Il y a toujours documentcloud.github.com/underscore qui a _.each et beaucoup d'autres fonctions utiles
w00t
1
cela signifie-t-il également que si la propriété length de mon objet $ .each échouera? par exemple x = {a: "1", b: "2", longueur: 3}.
Onur Topal
29

il existe des différences de performances selon le type de boucle que vous utilisez et le navigateur.

Par exemple:

for (var i = myArray.length-1; i >= 0; i--)

est presque deux fois plus rapide sur certains navigateurs que:

for (var i = 0; i < myArray.length; i++)

Cependant, à moins que vos tableaux soient ÉNORMES ou que vous les boucliez constamment, tous sont assez rapides. Je doute sérieusement que le bouclage de tableaux soit un goulot d'étranglement dans votre projet (ou pour tout autre projet d'ailleurs)

Gène
la source
5
Le stockage de "myArray.length" dans une variable avant le bouclage ferait-il disparaître la différence de performances? Ma supposition est "oui".
Tomalak
3
Non. 'MyArray.length' est une propriété, pas une méthode sur un objet - aucun calcul n'est effectué pour déterminer sa valeur. Stocker sa valeur dans une variable ne fera rien du tout.
Jason
3
Oui il y a. Les propriétés ne sont pas des variables; ils ont un code get / set.
ste
12
J'ai tendance à utiliserfor(var i = myArray.length; i--;)
Kevin
2
Code de clarté et de lisibilité. Pas pour ce qui se passe légèrement plus rapidement dans certains navigateurs lors de l'utilisation de tableaux absurdement massifs à l'heure actuelle. Les optimisations peuvent changer, mais la lisibilité de votre code (ou son absence) ne changera pas. Écrivez du code que les autres peuvent facilement suivre et laissez les optimiseurs rattraper leur retard.
aroth
26

Notez que la méthode native Array.forEach est désormais largement prise en charge .

Sam Dutton
la source
2
Qu'est ce que ça fait? A-t-il les problèmes mentionnés dans d'autres articles (bouclage des propriétés au lieu des éléments du tableau)? En outre, comme IE8 ne le prend pas en charge, il est un peu exagéré de dire qu'il est largement pris en charge.
Rauni Lillemets
2
autant que les utilisateurs de * nix le méprisent, IE8 est avant tout un utilisateur sous-windows7. c'est une partie massive du marché des navigateurs.
sbartell du
2
@Rauni - Je comprends votre point de vue, mais pour les appareils de bureau, le partage du navigateur IE est inférieur à 40%, selon en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table , et selon markethare.hitslink.com/… et d'autres sites, au moins 8% des navigateurs sont IE 9. En d'autres termes, Array.forEach est pris en charge par environ 70% des navigateurs de bureau, donc je ne pense pas que «largement pris en charge» ne soit pas raisonnable. Je n'ai pas vérifié, mais le support mobile (sur les navigateurs WebKit et Opera) peut être encore plus élevé. De toute évidence, il existe des variations géographiques considérables.
Sam Dutton
1
Merci pour la mise à jour. Je conviens que l'on pourrait dire qu'il est "largement soutenu". Le seul problème est que si l'utilisateur utilise cette méthode JS, il / elle doit toujours écrire une méthode de sauvegarde pour le cas si elle n'est pas prise en charge ..
Rauni Lillemets
1
@Rauni - vous pouvez utiliser es5-shim et es6-shim pour fournir automatiquement des méthodes de sauvegarde. github.com/es-shims/es5-shim
Michiel van der Blonk
24

Réponse mise à jour pour la version actuelle 2012 de tous les principaux navigateurs - Chrome, Firefox, IE9, Safari et Opera prennent en charge le tableau natif d'ES5 pour chaque.

Sauf si vous avez une raison de prendre en charge IE8 nativement (en gardant à l'esprit que le cadre ES5-shim ou Chrome peut être fourni à ces utilisateurs, ce qui fournira un environnement JS approprié), il est plus simple d'utiliser simplement la syntaxe appropriée du langage:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

La documentation complète pour array.forEach () est à MDN.

mikemaccana
la source
1
Vous devriez vraiment documenter les paramètres du callback: 1er la valeur de l'élément, 2e l'index de l'élément, 3e le tableau traversé
drewish
J'entends ce que vous dites, mais dans ce cas, une simplification excessive obscurcit toute la gamme des possibilités. Avoir à la fois l'index et la valeur signifie qu'il peut servir de remplacement à la fois pour ... in et pour chaque ... in - avec le bonus que vous n'avez pas à vous rappeler qui itère sur les clés ou les valeurs.
2012
1
@Cory: ES5 forEach peut être ajouté aux navigateurs ES3 hérités assez facilement. Moins de code est meilleur code.
mikemaccana
2
@nailer Peut-il être utilisé de manière interchangeable sur des tableaux et des objets?
hitautodestruct
1
@hitautodestruct Cela fait partie du prototype d'Array, pas d'Object. Généralement dans la communauté, les objets non-Array sont toujours itérés avec 'for (var key in object) {}'.
mikemaccana
14

Les deux ne sont pas identiques lorsque le tableau est clairsemé.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}
Mike Samuel
la source
14

Utilisation de forEach pour ignorer la chaîne de prototypes

Juste un addendum rapide à la réponse de @ nailer ci - dessus , l'utilisation de forEach avec Object.keys signifie que vous pouvez éviter d'itérer sur la chaîne de prototype sans avoir à utiliser hasOwnProperty.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});
meloncholy
la source
2
putain, c'est sournois. Cela valait la peine de lire 50 autres articles pour y arriver. obj = {"pink": "canards", red: "oies"}; Object.keys (obj) === ["rose", "rouge"]
Orwellophile
14

Je soutiens que vous devez choisir la méthode d'itération en fonction de vos besoins. Je vous suggère de ne pas en fait jamais une boucle par native Arrayavec la for instructure. C'est beaucoup plus lent et , comme Chase Seibert l'a souligné il y a un instant, il n'est pas compatible avec le framework Prototype.

Il existe une excellente référence sur différents styles de boucle que vous devez absolument consulter si vous travaillez avec JavaScript . Ne faites pas les premières optimisations, mais vous devriez garder ces choses quelque part à l'arrière de votre tête.

J'utiliserais for inpour obtenir toutes les propriétés d'un objet, ce qui est particulièrement utile lors du débogage de vos scripts. Par exemple, j'aime avoir cette ligne à portée de main lorsque j'explore un objet inconnu:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Il transfère le contenu de l'objet entier (avec les corps de méthode) dans mon journal Firebug. Très pratique.

Damir Zekić
la source
Le lien est maintenant rompu. Bien sûr, aimerait voir la référence, si quelqu'un a un autre lien.
Billbad
Ce n'est plus cassé.
Olli
Les boucles Foreach se cassent dans le prototype? Comme il est désormais couramment pris en charge, c'est quelque chose que le prototype devrait résoudre.
mvrak
7

voici quelque chose que j'ai fait.

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

voici comment vous l'utiliseriez,
cela fonctionnera sur les tableaux et les objets (comme une liste d'éléments HTML)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Je viens de faire cela, donc je suis ouvert aux suggestions :)


la source
Super truc - assez simple!
Chris
6

J'utiliserais les différentes méthodes en fonction de la façon dont je voulais référencer les éléments.

Utilisez foreach si vous voulez juste l'élément actuel.

Utilisez pour si vous avez besoin d'un indexeur pour effectuer des comparaisons relatives. (C'est-à-dire comment cela se compare-t-il à l'élément précédent / suivant?)

Je n'ai jamais remarqué de différence de performance. J'attendrais d'avoir un problème de performance avant de m'en inquiéter.

Matt Lacey
la source
Voir la réponse de Bnos ci-dessous - car ... ne fait pas ce que vous attendez ici et si vous l'utilisez, vous pourriez bien vous amuser. Pour mémoire, Prototype fait les choses correctement .
marcus.greasly
4

Avec for (var i dans myArray), vous pouvez également parcourir les objets, je contiendra le nom de la clé et vous pouvez accéder à la propriété via myArray [i] . De plus, toutes les méthodes que vous aurez ajoutées à l'objet seront également incluses dans la boucle, c'est-à-dire si vous utilisez un framework externe comme jQuery ou prototype, ou si vous ajoutez directement des méthodes aux prototypes d'objet, à un moment donné je pointerai vers ces méthodes.

pilsetnieks
la source
4

Fais attention!

Si vous avez plusieurs balises de script et que vous recherchez une information dans les attributs de balise par exemple, vous devez utiliser la propriété .length avec une boucle for car ce n'est pas un simple tableau mais un objet HTMLCollection.

https://developer.mozilla.org/en/DOM/HTMLCollection

Si vous utilisez l'instruction foreach pour (var i dans yourList), elle renverra les propriétés et les méthodes de HTMLCollection dans la plupart des navigateurs!

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

Même si getElementsByTagName doit retourner une NodeList, la plupart des navigateurs retournent une HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName

baptx
la source
3

Les boucles in in Arrays ne sont pas compatibles avec Prototype. Si vous pensez que vous pourriez avoir besoin d'utiliser cette bibliothèque à l'avenir, il serait logique de s'en tenir aux boucles.

http://www.prototypejs.org/api/array

Chase Seibert
la source
Oubliez "vous pourriez avoir besoin d'utiliser cette bibliothèque". Pensez plutôt "votre JS pourrait être inclus avec tout ce qui utilise cette bibliothèque", car les problèmes surviennent toujours.
ijw
3

J'ai vu des problèmes avec le "pour chacun" en utilisant des objets et des prototypes et des tableaux

ma compréhension est que le pour chacun est pour les propriétés des objets et non des tableaux

Benjamin Lee
la source
3

Si vous voulez vraiment accélérer votre code, qu'en est-il?

for( var i=0,j=null; j=array[i++]; foo(j) );

c'est un peu d'avoir la logique while dans l'instruction for et c'est moins redondant. Firefox a également Array.forEach et Array.filter

fabjoa
la source
2
Pourquoi cela accélérerait-il votre code? Je ne vois pas pourquoi réorganiser des déclarations comme celle-ci accélèrerait.
Rup
3

Un code plus court et meilleur selon jsperf est

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}
bormat
la source
1

Utilisez la boucle Array (). ForEach pour profiter du parallélisme

PazoozaTest Pazman
la source
4
JavaScript dans le navigateur est une boucle d'événements simultanée, Array.prototype.forEachil n'exécutera donc pas plusieurs appels au rappel en parallèle.
Mike Samuel
1

pour (;;) est pour les tableaux : [20,55,33]

for..in est pour les objets : {x: 20, y: 55: z: 33}

angelito
la source
0

Faites attention!!! J'utilise Chrome 22.0 sous Mac OS et j'ai des problèmes avec la pour chaque syntaxe.

Je ne sais pas s'il s'agit d'un problème de navigateur, d'un problème javascript ou d'une erreur dans le code, mais c'est TRÈS étrange. En dehors de l'objet, il fonctionne parfaitement.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works
Paulo Check
la source
0

Il y a une différence importante entre les deux. Le for-in itère sur les propriétés d'un objet, donc lorsque le cas est un tableau, il itérera non seulement sur ses éléments mais également sur la fonction "remove" qu'il possède.

for (var i = 0; i < myArray.length; i++) { 
    console.log(i) 
}

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

Vous pouvez utiliser le for-in avec un if(myArray.hasOwnProperty(i)). Pourtant, lors de l'itération sur des tableaux, je préfère toujours éviter cela et utiliser simplement l'instruction for (;;).

achecopar
la source
0

Bien qu'ils se ressemblent tous les deux, il existe une différence mineure:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

dans ce cas, la sortie est:

NORME POUR LA BOUCLE:

TABLEAU [0] = A

TABLEAU [1] = B

TABLEAU [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

alors que dans ce cas la sortie est:

BOUCLE FOR-IN:

TABLEAU [1] = B

TABLEAU [2] = C

TABLEAU [10] = D

TABLEAU [ABC] = 123

Yash Srivastava
la source
0

L'instruction for in permet de parcourir les noms de toutes les propriétés d'un objet. Malheureusement, il parcourt également tous les membres hérités de la chaîne de prototypes. Cela a le mauvais effet secondaire de servir des fonctions de méthode lorsque l'intérêt est dans les membres de données.

Fayaz
la source