Question Pourquoi utiliser "pour ... dans" avec itération de tableau une mauvaise idée?


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


1539
2018-02-01 09:46


origine


Réponses:


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 JavaScript Les bibliothèques peuvent faire des choses comme ceci, 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
*/


1341
2018-02-01 10:08



le for-in déclaration n'est pas une "mauvaise pratique", mais elle peut être mal utilisépar exemple, pour répéter sur des tableaux ou des objets de type tableau.

Le but de la for-in déclaration est de énumérer sur les propriétés de l'objet. Cette déclaration va remonter dans la chaîne du prototype, énumérant également hérité propriétés, une chose qui parfois n'est pas désiré.

En outre, l'ordre d'itération n'est pas garanti par la spécification, ce qui signifie que si vous voulez "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, dans JScript (IE <= 8), l'ordre d'énumération, même sur les objets Array, est défini au fur et à mesure que les propriétés ont été créées:

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 de propriétés héritées, si vous, par exemple, étendre la Array.prototype objet (comme certaines bibliothèques comme MooTools), les 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 à répéter sur des tableaux ou des objets de type tableau, la meilleure chose est d'utiliser un boucle séquentielle, comme un plaine for/while boucle.

Lorsque vous voulez énumérer seulement les propres propriétés d'un objet (ceux qui ne sont pas hérités), vous pouvez utiliser le hasOwnProperty méthode:

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

Et certaines personnes recommandent même d'appeler la méthode directement depuis Object.prototype pour é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
  }
}

356
2017-11-23 21:22



Il y a trois raisons pour lesquelles vous ne devriez pas utiliser for..in itérer sur les éléments du tableau:

  • for..in boucle sur toutes les propriétés propres et héritées de l'objet tableau qui ne sont pas DontEnum; cela signifie que si quelqu'un ajoute des propriétés à l'objet tableau spécifique (il y a des raisons valables pour cela - je l'ai fait moi-même) ou changé Array.prototype (ce qui est considéré comme une mauvaise pratique dans le code qui est supposé fonctionner correctement avec d'autres scripts), ces propriétés seront également réitérées; 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

  • c'est lent parce que vous devez marcher toutes les propriétés de l'objet tableau et de toute sa chaîne de prototype 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


101
2018-02-01 14:04



Parce que ... dans énumère à travers l'objet qui contient le tableau, pas le tableau lui-même. Si j'ajoute une fonction à la chaîne de prototypes de tableaux, cela sera également inclus. 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 = barre
myOwnFunction = function () {alert (this); }

Et puisque vous ne pouvez jamais être sûr que rien ne sera ajouté à la chaîne du 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 = barre

48
2018-02-01 10:08



Isolément, il n'y a rien de mal à utiliser le for-in sur les tableaux. Itérations sur les noms de propriété d'un objet, et dans le cas d'un tableau "out-of-the-box", les propriétés correspondent aux index de tableau. (Le built-in propertes comme length, toString et ainsi de suite ne sont pas inclus 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 sont pas, donc avec JQuery vous pouvez utiliser for-in en toute sécurité.

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

Un autre moyen d'itérer dans un tableau est d'utiliser 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 arr comme:

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

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


37
2018-02-01 10:34



En plus des raisons données dans les autres réponses, vous pouvez ne pas vouloir utiliser la structure "for ... in" si vous avez besoin de faire des calculs avec la variable counter car la boucle parcourt les noms des propriétés de l'objet et ainsi 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.


28
2017-09-02 02:29



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

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 montre:

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...of compte de 0 à 5, et ignore également Array.prototype.foo. Il montre tableau valeurs.

  • for...in listes seulement les 5, en ignorant les index de tableau non définis, mais en ajoutant foo. Il montre tableau noms de propriété.


24
2018-03-10 04:29



En dehors du fait que for...in boucles sur toutes les propriétés énumérables (qui est ne pas le même 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 commande d'énumérer les propriétés ... n'est pas spécifié...

(Emphase la mienne.)

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 dans l'ordre numérique. Ou dans l'ordre lexical (où "30" vient avant "4"! Gardez à l'esprit toutes les clés d'objet - et donc, tous les index de tableau - sont en fait des chaînes, ce qui est logique). Il pourrait les parcourir par bucket, s'il implémente des objets comme tables de hachage. Ou prenez n'importe lequel de cela et ajoutez "en arrière". Un navigateur pourrait même itérer au hasard et être conforme à la norme ECMA-262, à condition qu'elle ait visité chaque propriété une seule fois.

En pratique, la plupart des navigateurs préfèrent actuellement parcourir le même ordre. Mais il n'y a rien qui dise qu'ils doivent le faire. C'est spécifique à la mise en œuvre, et cela pourrait changer à tout moment si un autre moyen s'avérait beaucoup plus efficace.

D'une manière ou d'une autre, for...in porte avec elle aucune connotation d'ordre. Si vous vous souciez de l'ordre, soyez explicite et utilisez régulièrement for boucle avec un index.


21
2018-05-14 16:26



Réponse courte: Cela ne vaut tout simplement pas la peine.


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


Réponse longue: Cela ne vaut pas la peine, pour les raisons suivantes:

  • En utilisant for (var i in array) {} fera que 'array' sera interprété comme n'importe quel autre pur objet, traversant la chaîne de propriété de l'objet et, finalement, plus lent qu'un index basé for boucle.
  • Il n'est pas garanti de renvoyer les propriétés de l'objet dans un ordre séquentiel comme on pourrait s'y attendre.
  • En utilisant hasOwnProperty() ou isNaN() les vérifications pour filtrer les propriétés de l'objet sont un surcoût supplémentaire qui l'oblige à effectuer (encore plus) plus lentement. En outre, l'introduction d'une telle logique supplémentaire annule la raison principale de l'utiliser en premier lieu, c'est-à-dire en raison du format plus concis.

Pour ces raisons, un compromis acceptable entre performance et commodité n'existe même pas. Vraiment, il n'y a aucun avantage à moins que l'intention est de traiter le tableau comme pur objet et effectue des opérations sur les propriétés de l'objet tableau.


20
2018-03-14 07:12