Pourquoi l'utilisation de «for… in» avec l'itération de tableau est une mauvaise idée?

1824

On m'a dit de ne pas utiliser for...inde tableaux en JavaScript. Pourquoi pas?

lYriCAlsSH
la source
45
J'ai vu la question récente où quelqu'un vous a dit cela, mais ils ne visaient que les tableaux. Il est considéré comme une mauvaise pratique pour l'itération à travers des tableaux mais pas nécessairement pour l'itération à travers les membres d'un objet.
mmurch
19
Beaucoup de réponses avec des boucles "for" telles que 'for (var i = 0; i <hColl.length; i ++) {}' par rapport à 'var i = hColl.length; tandis que (i--) {} 'qui, lorsqu'il est possible d'utiliser cette dernière forme, est sensiblement plus rapide. Je sais que c'est tangentiel mais j'ai pensé ajouter ce morceau.
Mark Schultheiss
2
@MarkSchultheiss mais c'est une itération inverse. Existe-t-il une autre version d'itération directe plus rapide?
ma11hew28
5
@Wynand utilise le var i = hCol1.length; for (i;i;i--;) {}cache i car cela fera une différence et simplifiera le test. - plus le navigateur est ancien, plus il y a de différence entre foret whileTOUJOURS mettre en cache le compteur "i" - et bien sûr le négatif ne correspond pas toujours à la situation, et le négatif alors que obfuscate le code un peu pour certaines personnes. et notez var i = 1000; for (i; i; i--) {}et var b =1000 for (b; b--;) {}où je passe de 1000 à 1 et b passe de 999 à 0. - plus le navigateur est ancien, plus le temps tend à favoriser les performances.
Mark Schultheiss
9
Vous pouvez également être intelligent. for(var i = 0, l = myArray.length; i < l; ++i) ...est le plus rapide et le meilleur que vous puissiez obtenir avec l'itération directe.
Mathieu Amiot

Réponses:

1557

La raison en est qu'une construction:

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

peut parfois être totalement différent de l'autre:

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

Considérez également que les bibliothèques JavaScript peuvent faire des choses comme ça, qui affecteront n'importe quel tableau que vous créez:

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

Triptyque
la source
146
Historiquement, certains navigateurs ont même itéré sur «longueur», «toString», etc.!
bobince
398
N'oubliez pas d'utiliser (var x in a)plutôt que (x in a)- ne voulez pas créer un global.
Chris Morgan
78
Le premier problème n'est pas une raison pour laquelle il est mauvais, seulement une différence de sémantique. Le deuxième problème me semble être une raison (en plus des conflits entre bibliothèques faisant de même) que la modification du prototype d'un type de données intégré est mauvaise, plutôt que celle de..in est mauvaise.
Stewart
86
@Stewart: Tous les objets dans JS sont associatifs. Un tableau JS est un objet, donc oui, il est aussi associatif, mais ce n'est pas à ça qu'il sert. Si vous souhaitez parcourir l'item d'un objet clés d' , utilisez for (var key in object). Cependant, si vous souhaitez parcourir les éléments d' un tableau , utilisez for(var i = 0; i < array.length; i += 1).
Martijn
42
Vous avez dit pour le premier exemple, qu'il Itère sur des index numériques de 0 à 4, comme tout le monde s'y attend , je m'attends à ce qu'il itère de 0 à 5 ! Puisque si vous ajoutez un élément en position 5, le tableau aura 6 éléments (5 d'entre eux non définis).
stivlo
393

L' for-ininstruction en soi n'est pas une "mauvaise pratique", mais elle peut être mal utilisée , par exemple, pour itérer sur des tableaux ou des objets de type tableau.

Le but de la for-indéclaration est de énumérer les propriétés des objets. Cette déclaration remontera dans la chaîne du prototype, énumérant également les propriétés héritées , une chose qui n'est parfois pas souhaitée.

En outre, l'ordre d'itération n'est pas garanti par la spécification, ce qui signifie que si vous souhaitez "itérer" un objet tableau, avec cette instruction, vous ne pouvez pas être sûr que les propriétés (index de tableau) seront visitées dans l'ordre numérique.

Par exemple, en JScript (IE <= 8), l'ordre d'énumération même sur les objets Array est défini lors de la création des propriétés:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

En outre, en parlant des propriétés héritées, si vous, par exemple, étendez la propriété Array.prototype objet (comme certaines bibliothèques comme le font MooTools), ces propriétés seront également énumérées:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

Comme je l'ai déjà dit pour parcourir les tableaux ou les objets de type tableau, la meilleure chose à faire est d'utiliser un boucle séquentielle , telle qu'une boucle plain-old for/ while.

Lorsque vous souhaitez énumérer uniquement les propres propriétés d'un objet (celles qui ne sont pas héritées), vous pouvez utiliser la hasOwnPropertyméthode:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}

Et certaines personnes recommandent même d'appeler directement la méthode Object.prototypepour éviter d'avoir des problèmes si quelqu'un ajoute une propriété nommée hasOwnPropertyà notre objet:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}
CMS
la source
10
Voir aussi le post de David Humphrey Itération sur les objets en JavaScript rapidement - pour les tableaux for..inest beaucoup plus lent que les boucles "normales".
Chris Morgan
17
Question sur le dernier point concernant "hasOwnProperty": Si quelqu'un remplace "hasOwnProperty" sur un objet, vous aurez des problèmes. Mais n'aurez-vous pas les mêmes problèmes si quelqu'un remplace "Object.prototype.hasOwnProperty"? De toute façon, ils vous foutent en l'air et ce n'est pas votre responsabilité, non?
Scott Rippey
Vous dites que ce for..inn'est pas une mauvaise pratique, mais qu'elle peut être utilisée à mauvais escient. Avez-vous un exemple concret de bonnes pratiques, où vous vouliez vraiment parcourir toutes les propriétés d'un objet, y compris les propriétés héritées?
rjmunro
4
@ScottRippey: Si vous voulez y aller: youtube.com/watch?v=FrFUI591WhI
Nathan Wall
avec cette réponse, j'ai trouvé que peut accéder à la valeur avecfor (var p in array) { array[p]; }
equiman
117

Il y a trois raisons pour lesquelles vous ne devriez pas utiliser for..inpour parcourir les éléments du tableau:

  • for..infera une boucle sur toutes les propriétés propres et héritées de l'objet tableau qui ne le sont pas DontEnum; cela signifie que si quelqu'un ajoute des propriétés à l'objet de tableau spécifique (il y a des raisons valables - je l'ai fait moi-même) ou modifié Array.prototype(ce qui est considéré comme une mauvaise pratique dans le code qui est censé fonctionner correctement avec d'autres scripts), ces propriétés être réitéré également; les propriétés héritées peuvent être exclues en vérifiant hasOwnProperty(), mais cela ne vous aidera pas avec les propriétés définies dans l'objet tableau lui-même

  • for..in n'est pas garanti pour préserver l'ordre des éléments

  • il est lent car vous devez parcourir toutes les propriétés de l'objet tableau et de toute sa chaîne de prototypes et n'obtiendra toujours que le nom de la propriété, c'est-à-dire que pour obtenir la valeur, une recherche supplémentaire sera nécessaire

Christoph
la source
55

Parce que for ... in énumère l'objet qui contient le tableau, pas le tableau lui-même. Si j'ajoute une fonction à la chaîne de prototypes de tableaux, elle sera également incluse. C'est à dire

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

Cela écrira:

0 = foo
1 = bar
myOwnFunction = function () {alert (this); }

Et comme vous ne pouvez jamais être sûr que rien ne sera ajouté à la chaîne de prototype, utilisez simplement une boucle for pour énumérer le tableau:

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

Cela écrira:

0 = foo
1 = bar
Pim Jager
la source
16
Les tableaux sont des objets, il n'y a pas "d'objet qui contient le tableau".
RobG
41

Isolément, il n'y a rien de mal à utiliser for-in sur les tableaux. For-in parcourt les noms de propriété d'un objet et, dans le cas d'un tableau "prêt à l'emploi", les propriétés correspondent aux index du tableau. (Les propriétés intégrées telles que length, toStringetc. ne sont pas incluses dans l'itération.)

Cependant, si votre code (ou le framework que vous utilisez) ajoute des propriétés personnalisées aux tableaux ou au prototype de tableau, ces propriétés seront incluses dans l'itération, ce qui n'est probablement pas ce que vous voulez.

Certains frameworks JS, comme Prototype, modifient le prototype Array. D'autres frameworks comme JQuery ne le font pas, donc avec JQuery vous pouvez utiliser en toute sécurité for-in.

Si vous avez un doute, vous ne devriez probablement pas utiliser for-in.

Une autre façon d'itérer à travers un tableau utilise une boucle for:

for (var ix=0;ix<arr.length;ix++) alert(ix);

Cependant, cela a un problème différent. Le problème est qu'un tableau JavaScript peut avoir des "trous". Si vous définissez arrcomme:

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

Ensuite, le tableau a deux éléments, mais une longueur de 101. L'utilisation de for-in produira deux index, tandis que la boucle for donnera 101 index, où le 99 a une valeur de undefined.

JacquesB
la source
37

À partir de 2016 (ES6), nous pouvons utiliser for…ofpour l'itération de tableau, comme John Slegers l'a déjà remarqué.

Je voudrais juste ajouter ce code de démonstration simple, pour rendre les choses plus claires:

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
    }

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
    }

La console affiche:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

En d'autres termes:

  • for...ofcompte de 0 à 5 et ignore également Array.prototype.foo. Il montre les valeurs du tableau .

  • for...inrépertorie uniquement les 5, en ignorant les index de tableau non définis, mais en ajoutant foo. Il affiche les noms des propriétés du tableau .

MarcG
la source
32

Réponse courte: ça n'en vaut pas la peine.


Réponse plus longue: cela n'en vaut pas la peine, même si l'ordre séquentiel des éléments et des performances optimales ne sont pas nécessaires.


Réponse longue: ça n'en vaut pas la peine ...

  • L'utilisation for (var property in array)entraînera arrayune itération sur un objet , traversant la chaîne de prototypes d'objet et, finalement, plus lente qu'une forboucle basée sur un index .
  • for (... in ...) n'est pas garanti de renvoyer les propriétés de l'objet dans un ordre séquentiel, comme on pourrait s'y attendre.
  • L'utilisation hasOwnProperty()et les !isNaN()vérifications pour filtrer les propriétés de l'objet sont une surcharge supplémentaire qui le rend encore plus lent et annule la raison principale de son utilisation en premier lieu, c'est-à-dire en raison du format plus concis.

Pour ces raisons, il n'existe même pas de compromis acceptable entre performances et commodité. Il n'y a vraiment aucun avantage à moins que l'intention soit de gérer le tableau en tant qu'objet et d'effectuer des opérations sur les propriétés d'objet du tableau.

WynandB
la source
31

En plus des raisons données dans d'autres réponses, vous ne voudrez peut-être pas utiliser la structure "for ... in" si vous avez besoin de faire des calculs avec la variable compteur car la boucle parcourt les noms des propriétés de l'objet et donc la variable est une chaîne.

Par exemple,

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

écrirai

0, number, 1
1, number, 2
...

tandis que,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

écrirai

0, string, 01
1, string, 11
...

Bien sûr, cela peut facilement être surmonté en incluant

ii = parseInt(ii);

dans la boucle, mais la première structure est plus directe.

ctmiddle
la source
6
Vous pouvez utiliser le préfixe +au lieu de parseIntsauf si vous avez vraiment besoin d'un entier ou d'ignorer les caractères non valides.
Konrad Borowski
De plus, l'utilisation parseInt()n'est pas recommandée. Essayez parseInt("025");et il va échouer.
Derek 朕 會 功夫
6
@Derek 朕 會 功夫 - vous pouvez certainement utiliser parseInt. Le problème est que si vous n'incluez pas la base, les anciens navigateurs peuvent essayer d'interpréter le nombre (ainsi 025 devient octal). Cela a été corrigé dans ECMAScript 5 mais cela se produit toujours pour les nombres commençant par "0x" (il interprète le nombre comme hex). Pour être sûr, utilisez le radix pour spécifier le nombre comme ça parseInt("025", 10)- qui spécifie la base 10.
IAmTimCorey
23

Mis à part le fait que for... inboucle sur toutes les propriétés énumérables (ce qui n'est pas la même chose que "tous les éléments du tableau"!), Voir http://www.ecma-international.org/publications/files/ECMA-ST/Ecma -262.pdf , section 12.6.4 (5e édition) ou 13.7.5.15 (7e édition):

La mécanique et l' ordre d'énumération des propriétés ... n'est pas spécifié ...

(Je souligne.)

Cela signifie que si un navigateur le voulait, il pourrait parcourir les propriétés dans l'ordre dans lequel elles ont été insérées. Ou par ordre numérique. Ou dans l'ordre lexical (où "30" précède "4"! Gardez à l'esprit que toutes les clés d'objet - et donc tous les index de tableau - sont en fait des chaînes, ce qui est parfaitement logique). Il pourrait les parcourir par compartiment, s'il implémentait des objets sous forme de tables de hachage. Ou prenez tout cela et ajoutez "à l'envers". Un navigateur peut même itérer de façon aléatoire et être conforme à la norme ECMA-262, tant qu'il visite chaque propriété une seule fois.

En pratique, la plupart des navigateurs aiment actuellement répéter à peu près le même ordre. Mais rien ne dit qu'ils doivent le faire. C'est spécifique à l'implémentation et pourrait changer à tout moment si une autre méthode s'avérait beaucoup plus efficace.

Quoi qu'il en soit, for... ne incomporte aucune connotation d'ordre. Si vous vous souciez de l'ordre, soyez explicite à ce sujet et utilisez une forboucle régulière avec un index.

cHao
la source
18

Principalement deux raisons:

Une

Comme d'autres l'ont dit, vous pourriez obtenir des clés qui ne sont pas dans votre tableau ou qui sont héritées du prototype. Donc, si, disons, une bibliothèque ajoute une propriété aux prototypes Array ou Object:

Array.prototype.someProperty = true

Vous l'obtiendrez dans le cadre de chaque tableau:

for(var item in [1,2,3]){
  console.log(item) // will log 1,2,3 but also "someProperty"
}

vous pouvez résoudre ce problème avec la méthode hasOwnProperty:

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

mais cela est vrai pour l'itération sur n'importe quel objet avec une boucle for-in.

Deux

Habituellement, l'ordre des éléments dans un tableau est important, mais la boucle for-in ne sera pas nécessairement itérée dans le bon ordre, c'est parce qu'elle traite le tableau comme un objet, ce qui est la façon dont il est implémenté dans JS, et non comme un tableau. Cela semble être une petite chose, mais cela peut vraiment bousiller les applications et est difficile à déboguer.

Lior
la source
2
Object.keys(a).forEach( function(item) { console.log(item) } )itérer sur un tableau de clés de propriétés propres, pas celles héritées du prototype.
Qwerty
2
Certes, mais comme la boucle for-in, elle ne sera pas nécessairement dans le bon ordre d'index. De plus, cela ne fonctionnera pas sur les anciens navigateurs ne prenant pas en charge ES5.
Lior
Vous pouvez apprendre à ces navigateurs array.forEachen insérant un certain code dans vos scripts. Voir Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Qwerty
Bien sûr, mais alors vous manipulez le prototype, et ce n'est pas toujours une bonne idée ... et encore, vous avez le problème de la commande ...
Lior
Et, bien sûr, la troisième raison: les tableaux clairsemés.
un meilleur olivier
16

Parce qu'il énumère les champs d'objet, pas les index. Vous pouvez obtenir de la valeur avec l'index "longueur" et je doute que vous le vouliez.

vava
la source
Quelle est donc la meilleure façon de procéder?
lYriCAlsSH
3
for (var i = 0; i <arr.length; i ++) {}
vava
3
Dans firefox 3, vous pouvez également utiliser arr.forEach ou for (var [i, v] dans Iterator (arr)) {} mais aucun de ces travaux dans IE, bien que vous puissiez écrire vous-même la méthode forEach.
vava
et pratiquement chaque bibliothèque a sa propre méthode pour cela aussi.
vava
5
Cette réponse est fausse. "longueur" ne sera pas inclus dans l'itération pour-dans. Seules les propriétés que vous ajoutez vous-même sont incluses.
JacquesB
16

Je ne pense pas avoir grand-chose à ajouter, par exemple. Réponse de Triptyque ou réponse de CMS sur les raisons pour lesquelles l'utilisation for...indoit être évitée dans certains cas.

Je voudrais cependant ajouter que dans les navigateurs modernes, il existe une alternative à for...incela qui peut être utilisée dans les cas où elle for...inne peut pas être utilisée. Cette alternative est for...of:

for (var item of items) {
    console.log(item);
}

Remarque :

Malheureusement, aucune version d'Internet Explorer ne prend en charge for...of( Edge 12+ le fait), vous devrez donc attendre un peu plus longtemps pour pouvoir l'utiliser dans votre code de production côté client. Cependant, il doit être sûr de l'utiliser dans votre code JS côté serveur (si vous utilisez Node.js ).

John Slegers
la source
@georgeawg Vous vouliez dire for-of, non for-in, non?
ᆼ ᆺ ᆼ
15

Le problème avec for ... in ...- et cela ne devient un problème que lorsqu'un programmeur ne comprend pas vraiment la langue; ce n'est pas vraiment un bug ou quoi que ce soit - c'est qu'il itère sur tous les membres d'un objet (enfin, tous les membres énumérables , mais c'est un détail pour l'instant). Lorsque vous souhaitez parcourir uniquement les propriétés indexées d'un tableau, le seul moyen garanti de conserver une cohérence sémantique consiste à utiliser un index entier (c'est-à-dire une for (var i = 0; i < array.length; ++i)boucle de style).

Tout objet peut avoir des propriétés arbitraires qui lui sont associées. Il n'y aurait rien de terrible à charger des propriétés supplémentaires sur une instance de tableau, en particulier. Le code qui veut voir uniquement les propriétés de type tableau indexé doit donc s'en tenir à un index entier. Un code qui est pleinement conscient de ce qui for ... infait et a vraiment besoin de voir toutes les propriétés, eh bien, ça va aussi.

Pointu
la source
Belle explication pointue. Juste curieux. Si j'avais un tableau qui était à l'intérieur d'un objet sous des propriétés de multiplication et que for in, par rapport à une boucle for normale, ces tableaux seraient itérés? (ce qui serait essentiellement une performance lente, non?)
NiCk Newman
2
@NiCkNewman bien l'objet que vous référencez après indans une for ... inboucle sera juste
Pointy
Je vois. Juste curieux parce que j'ai des objets et des tableaux à l'intérieur de mon objet de jeu principal et je me demandais si pour la manche ce serait plus douloureux que simplement une boucle for régulière sur les index.
NiCk Newman
@NiCkNewman bien le thème de toute cette question est que vous ne devriez pas utiliser for ... insur les tableaux; il y a de nombreuses bonnes raisons de ne pas le faire. Ce n'est pas tant un problème de performances qu'un problème de type "assurez-vous qu'il ne casse pas".
Pointy
Eh bien, mes objets sont stockés dans un tableau techniquement, c'est pourquoi j'étais inquiet, quelque chose comme [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]:, mais oui, je comprends.
NiCk Newman
9

De plus, en raison de la sémantique, la façon dont for, inles tableaux sont traités (c'est-à-dire les mêmes que tout autre objet JavaScript) n'est pas alignée avec d'autres langages populaires.

// 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"
matpop
la source
9

TL&DR: Utiliser la for inboucle dans les tableaux n'est pas mal, en fait bien au contraire.

Je pense que la for inboucle est un joyau de JS si elle est utilisée correctement dans les tableaux. Vous êtes censé avoir un contrôle total sur votre logiciel et savoir ce que vous faites. Voyons les inconvénients mentionnés et réfutons-les un par un.

  1. Il parcourt également les propriétés héritées: Tout d'abord, toutes les extensions de la Array.prototypedoivent avoir été effectuées à l'aide de Object.defineProperty()et leur enumerabledescripteur doit être défini sur false. Toute bibliothèque qui ne le fait pas ne doit pas du tout être utilisée.
  2. Les propriétés que vous ajoutez ultérieurement à la chaîne d'héritage sont comptées: lors de la sous-classification de tableau par Object.setPrototypeOfou par classe extend. Vous devez utiliser à nouveau Object.defineProperty()que par des ensembles par défaut les writable, enumerableet les configurabledescripteurs de propriété à false. Voyons un exemple de sous-classement de tableau ici ...

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

Donc, vous voyez ... la for inboucle est maintenant sûre puisque vous vous souciez de votre code.

  1. La for inboucle est lente: Hell no. C'est de loin la méthode d'itération la plus rapide si vous parcourez des tableaux clairsemés qui sont nécessaires de temps en temps. Il s'agit de l'une des astuces de performance les plus importantes à connaître. Voyons un exemple. Nous allons boucler sur un tableau clairsemé.

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

Redu
la source
@Ravi Shanker Reddy Bonne configuration de l'analyse comparative. Comme je l'ai mentionné dans ma réponse, la for inboucle éclipse les autres "si" le tableau est clairsemé et plus s'il grossit. J'ai donc réorganisé le test de banc pour un tableau clairsemé arr, de taille ~ 10000 avec seulement 50 éléments choisis [42,"test",{t:1},null, void 0]au hasard parmi des indices aléatoires. Vous remarquerez immédiatement la différence. - >> Vérifiez ici << - .
Redu
8

En plus des autres problèmes, la syntaxe "for..in" est probablement plus lente, car l'index est une chaîne, pas un entier.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'
dc1
la source
Peu importe probablement beaucoup. Les éléments de tableau sont les propriétés d'un objet basé sur un tableau ou semblable à un tableau, et toutes les propriétés d'objet ont des clés de chaîne. À moins que votre moteur JS ne l'optimise d'une manière ou d'une autre, même si vous utilisiez un nombre, il finirait par être transformé en chaîne pour la recherche.
cHao
Indépendamment de tout problème de performances, si vous êtes nouveau dans JavaScript, utilisez var i in aet attendez-vous à ce que l'index soit un entier, alors faire quelque chose comme a[i+offset] = <value>cela mettra des valeurs complètement aux mauvais endroits. ("1" + 1 == "11").
szmoore
8

Un aspect important est que for...inseulement itère sur les propriétés contenues dans un objet dont l' attribut de propriété énumérable est défini sur true. Donc, si l'on tente de parcourir un objet en utilisantfor...in alors des propriétés arbitraires peuvent être manquées si leur attribut de propriété énumérable est faux. Il est tout à fait possible de modifier l'attribut de propriété énumérable pour les objets Array normaux afin que certains éléments ne soient pas énumérés. Bien qu'en général, les attributs de propriété tendent à s'appliquer aux propriétés de fonction au sein d'un objet.

On peut vérifier la valeur de l'attribut de propriété énumérable d'une propriété en:

myobject.propertyIsEnumerable('myproperty')

Ou pour obtenir les quatre attributs de propriété:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Il s'agit d'une fonctionnalité disponible dans ECMAScript 5 - dans les versions antérieures, il n'était pas possible de modifier la valeur de l'attribut de propriété énumérable (elle était toujours définie sur true).

Pierz
la source
8

Le for/ infonctionne avec deux types de variables: les tables de hachage (tableaux associatifs) et le tableau (non associatif).

JavaScript déterminera automatiquement la façon dont son passe à travers les éléments. Donc, si vous savez que votre tableau est vraiment non associatif, vous pouvez utiliser for (var i=0; i<=arrayLen; i++)et ignorer l'itération d'auto-détection.

Mais à mon avis, il vaut mieux utiliser for/ in, le processus requis pour cette détection automatique est très petit.

Une vraie réponse dépendra de la façon dont le navigateur analysera / interprétera le code JavaScript. Il peut changer d'un navigateur à l'autre.

Je ne peux pas penser à d'autres fins pour ne pas utiliser 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]);
Ricardo
la source
true, sauf si vous utilisez des objets prototypés. ;) ci
Ricardo
C'est parce que Arrayc'est Objecttrop
Consultation gratuite
2
for ... infonctionne avec des objets. La détection automatique n'existe pas.
un meilleur olivier
7

Parce qu'il itérera sur les propriétés appartenant aux objets de la chaîne du prototype si vous ne faites pas attention.

Vous pouvez utiliser for.. in, assurez-vous simplement de vérifier chaque propriété avec hasOwnProperty .

JAL
la source
2
Pas assez - il est tout à fait correct d'ajouter des propriétés nommées arbitraires aux instances de tableau, et celles-ci seront testées à truepartir des hasOwnProperty()vérifications.
Pointy
Bon point, merci. Je n'ai jamais été assez idiot pour faire ça à un tableau moi-même, donc je n'y ai pas pensé!
JAL
1
@Pointy Je n'ai pas testé cela, mais peut-être que cela peut être surmonté en utilisant une isNaNvérification sur chaque nom de propriété.
WynandB
1
@Wynand idée intéressante; Cependant, je ne vois pas vraiment pourquoi cela en vaut la peine quand itérer avec un simple index numérique est si facile.
Pointy
@WynandB désolé pour la bosse, mais je pense qu'une correction est en ordre: isNaNest pour vérifier si une variable est la valeur spéciale NaN ou non, elle ne peut pas être utilisée pour vérifier des `` choses autres que des nombres '' (vous pouvez aller avec un régulier typeof pour cela).
doldt
6

Ce n'est pas nécessairement mauvais (en fonction de ce que vous faites), mais dans le cas des tableaux, si quelque chose a été ajouté Array.prototype, vous obtiendrez des résultats étranges. Où vous vous attendez à ce que cette boucle s'exécute trois fois:

var arr = ['a','b','c'];
for (var key in arr) { ... }

Si une fonction appelée helpfulUtilityMethoda été ajouté à Array« s prototype, votre boucle finirait quatre fois en cours d' exécution: keyserait 0, 1, 2et helpfulUtilityMethod. Si vous ne vous attendiez qu'à des entiers, oups.

josh3736
la source
6

Vous devez utiliser for(var x in y)uniquement sur les listes de propriétés, pas sur les objets (comme expliqué ci-dessus).

user268396
la source
13
Juste une note à propos de SO - il n'y a pas de "ci-dessus" car les commentaires changent constamment l'ordre sur la page. Donc, nous ne savons pas vraiment de quel commentaire vous parlez. Il est bon de dire "dans le commentaire de x personne" pour cette raison.
JAL
@JAL ... ou ajoutez le permalien à la réponse.
WynandB
5

Utiliser la for...inboucle pour un tableau n'est pas faux, bien que je devine pourquoi quelqu'un vous a dit que:

1.) Il existe déjà une fonction ou méthode d'ordre supérieur, qui a cet objectif pour un tableau, mais a plus de fonctionnalités et une syntaxe plus légère, appelée «forEach»: Array.prototype.forEach(function(element, index, array) {} );

2.) Les tableaux ont toujours une longueur, mais for...inet forEachne pas exécuter une fonction pour une valeur qui est'undefined' , seulement pour les indices qui ont une valeur définie. Donc, si vous n'affectez qu'une seule valeur, ces boucles n'exécuteront une fonction qu'une seule fois, mais comme un tableau est énuméré, il aura toujours une longueur jusqu'à l'index le plus élevé qui a une valeur définie, mais cette longueur peut passer inaperçue lors de l'utilisation de ces boucles.

3.) La norme for loop exécutera une fonction autant de fois que vous le définissez dans les paramètres, et comme un tableau est numéroté, il est plus logique de définir combien de fois vous voulez exécuter une fonction. Contrairement aux autres boucles, la boucle for peut alors exécuter une fonction pour chaque index du tableau, que la valeur soit définie ou non.

En substance, vous pouvez utiliser n'importe quelle boucle, mais vous devez vous rappeler exactement comment ils fonctionnent. Comprendre les conditions dans lesquelles les différentes boucles sont réitérées, leurs fonctionnalités distinctes et se rendre compte qu'elles seront plus ou moins appropriées pour différents scénarios.

En outre, il peut être considéré comme une meilleure pratique d'utiliser la forEachméthode que la for...inboucle en général, car elle est plus facile à écrire et a plus de fonctionnalités, vous pouvez donc avoir l'habitude d'utiliser uniquement cette méthode et la norme pour, mais votre appel.

Voir ci-dessous que les deux premières boucles n'exécutent les instructions console.log qu'une seule fois, tandis que la norme for loop exécute la fonction autant de fois que spécifié, dans ce cas, 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]
mrmaclean89
la source
4

Voici les raisons pour lesquelles il s'agit (généralement) d'une mauvaise pratique:

  1. for...inles boucles parcourent toutes leurs propres propriétés énumérables et les propriétés énumérables de leurs prototypes. Habituellement, dans une itération de tableau, nous voulons seulement parcourir le tableau lui-même. Et même si vous-même n'ajoutez rien au tableau, vos bibliothèques ou votre infrastructure peuvent ajouter quelque chose.

Exemple :

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...inles boucles ne garantissent pas un ordre d'itération spécifique . Bien que l'ordre soit généralement vu dans la plupart des navigateurs modernes de nos jours, il n'y a toujours pas de garantie à 100%.
  2. for...inles boucles ignorent les undefinedéléments du tableau, c'est-à-dire les éléments du tableau qui n'ont pas encore été attribués.

Exemple ::

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}

Willem van der Veen
la source
2

for ... in est utile lorsque vous travaillez sur un objet en JavaScript, mais pas pour un tableau, mais nous ne pouvons toujours pas dire que c'est une mauvaise façon, mais ce n'est pas recommandé, regardez cet exemple ci-dessous en utilisant for ... in loop:

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

OK, faisons-le avec Array maintenant:

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

Comme vous voyez le même résultat ...

Mais essayons quelque chose, créons un prototype pour Array ...

Array.prototype.someoneelse = "someoneelse";

Nous créons maintenant un nouveau tableau ();

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

Vous voyez quelqu'un d'autre !!! ... Nous faisons en fait une boucle à travers le nouvel objet Array dans ce cas!

C'est donc l'une des raisons pour lesquelles nous devons utiliser for..in soigneusement, mais ce n'est pas toujours le cas ...

Alireza
la source
2

Une boucle for ... in énumère toujours les clés. Les clés de propriétés des objets sont toujours String, même les propriétés indexées d'un tableau:

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

bien que cela ne soit pas spécifiquement abordé par cette question, j'ajouterais qu'il y a une très bonne raison de ne jamais l'utiliser pour ... avec un NodeList(comme on l'obtiendrait à partir d'un querySelectorAllappel, car il ne voit pas du tout les éléments retournés, à la place itérer uniquement sur les propriétés NodeList.

dans le cas d'un seul résultat, j'ai obtenu:

var nodes = document.querySelectorAll(selector);
nodes
 NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

ce qui explique pourquoi mon for (node in nodes) node.href = newLink;échec.

jcomeau_ictx
la source