web-dev-qa-db-fra.com

Que signifie l'erreur JSLint "corps d'un for dans doit être encapsulé dans une instruction if"?

J'ai utilisé JSLint sur un de mes fichiers JavaScript. Il a jeté l'erreur:

for( ind in evtListeners ) {

Problème à la ligne 41, caractère 9: le corps du pour doit être placé dans une instruction if pour filtrer les propriétés indésirables du prototype.

Qu'est-ce que ça veut dire?

231
jrharshath

Tout d'abord, jamais utilisez une boucle for in Pour énumérer un tableau. Jamais. Utilisez le bon vieux for(var i = 0; i<arr.length; i++).

La raison derrière ceci est la suivante: chaque objet en JavaScript a un champ spécial appelé prototype. Tout ce que vous ajoutez à ce champ sera accessible sur chaque objet de ce type. Supposons que vous souhaitiez que toutes les baies aient une nouvelle fonction nommée filter_0 Qui filtrera les zéros.

Array.prototype.filter_0 = function() {
    var res = [];
    for (var i = 0; i < this.length; i++) {
        if (this[i] != 0) {
            res.Push(this[i]);
        }
    }
    return res;
};

console.log([0, 5, 0, 3, 0, 1, 0].filter_0());
//prints [5,3,1]

C'est un moyen standard d'étendre des objets et d'ajouter de nouvelles méthodes. Beaucoup de bibliothèques le font. Cependant, regardons comment for in Fonctionne maintenant:

var listeners = ["a", "b", "c"];
for (o in listeners) {
    console.log(o);
}
//prints:
//  0
//  1
//  2
//  filter_0

Est-ce que tu vois? Soudainement, filter_0 est un autre index de tableau. Bien sûr, ce n'est pas vraiment un index numérique, mais for in Énumère à travers des champs d'objet, pas seulement des index numériques. Nous énumérons donc maintenant tous les index numériques et filter_0. Mais filter_0 N'est pas un champ d'un objet tableau particulier, chaque objet tableau possède maintenant cette propriété.

Heureusement, tous les objets ont une méthode hasOwnProperty qui vérifie si ce champ appartient vraiment à l'objet lui-même ou s'il est simplement hérité de la chaîne de prototypes et appartient donc à tous les objets de ce type.

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}
 //prints:
 //  0
 //  1
 //  2

Notez que bien que ce code fonctionne comme prévu pour les tableaux, vous ne devriez jamais, jamais, utiliser for in Et for each in Pour les tableaux. Rappelez-vous que for in Énumère les champs d'un objet, pas les index ni les valeurs de tableaux.

var listeners = ["a", "b", "c"];
listeners.happy = "Happy debugging";

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}

 //prints:
 //  0
 //  1
 //  2
 //  happy
411
vava

Douglas Crockford, l'auteur de jslint a écrit (et parlé) à propos de cette question à plusieurs reprises. Il y a une section sur this page de son site web qui couvre ceci:

pour Statement

Une classe d'énoncés doit avoir la forme suivante:

for (initialization; condition; update) {
    statements
}

for (variable in object) {
    if (filter) {
        statements
    } 
}

La première forme doit être utilisée avec des tableaux et avec des boucles d'un nombre prédéterminé d'itérations.

Le second formulaire doit être utilisé avec des objets. Sachez que les membres ajoutés au prototype de l'objet seront inclus dans l'énumération. Il est sage de programmer de manière défensive en utilisant la méthode hasOwnProperty pour distinguer les vrais membres de l'objet:

for (variable in object) {
    if (object.hasOwnProperty(variable)) {
        statements
    } 
}

Crockford a également une série de vidéos sur le théâtre YUI où il en parle. La série de vidéos/discussions sur le javascript de Crockford est un must si vous êtes un peu sérieux au sujet de javascript.

86
Breton

Mauvais: (jsHint va lancer une erreur)

for (var name in item) {
    console.log(item[name]);
}

Bien:

for (var name in item) {
  if (item.hasOwnProperty(name)) {
    console.log(item[name]);
  }
}
17
wangchi

La réponse de Vava est sur la marque. Si vous utilisez jQuery, la fonction $.each() s'en charge, il est donc plus sûr de l'utiliser.

$.each(evtListeners, function(index, elem) {
    // your code
});
9
HRJ

@all - tout dans JavaScript est un objet (), aussi des déclarations comme "n'utilisez ceci que sur des objets" sont un peu trompeuses. De plus, JavaScript n'est pas fortement typé, ce qui signifie que 1 == "1" est vrai (bien que 1 === "1" ne l'ait pas, Crockford est un gros consommateur). En ce qui concerne le concept progromatique de tableaux dans JS, la définition est importante.

@Brenton - Pas besoin d'être un dictateur de terminologie; "tableau associatif", "dictionnaire", "hachage", "objet", ces concepts de programmation s'appliquent tous à une structure dans JS. Ce sont des paires nom (clé, index), où la valeur peut être n’importe quel autre objet (les chaînes sont aussi des objets)

Donc, new Array() est identique à []

new Object() est à peu près similaire à {}

var myarray = [];

Crée une structure qui est un tableau avec la restriction que tous les index (ou clés) doivent être un nombre entier. Il permet également l’affectation automatique de nouveaux index via .Push ()

var myarray = ["one","two","three"];

Est en effet mieux traité via for(initialization;condition;update){

Mais qu'en est-il:

var myarray = [];
myarray[100] = "foo";
myarray.Push("bar");

Essaye ça:

var myarray = [], i;
myarray[100] = "foo";
myarray.Push("bar");
myarray[150] = "baz";
myarray.Push("qux");
alert(myarray.length);
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

Peut-être pas la meilleure utilisation d'un tableau, mais juste une illustration que les choses ne sont pas toujours claires.

Si vous connaissez vos clés, et certainement si elles ne sont pas des nombres entiers, votre seule option de type tableau comme structure est l'objet.

var i, myarray= {
   "first":"john",
   "last":"doe",
   100:"foo",
   150:"baz"
};
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}
7
wade

C'est sûrement un peu extrême à dire

... n'utilisez jamais une boucle for in pour énumérer un tableau. Jamais. Utilisez good old pour (var i = 0; i <arr.length; i ++)

?

Il convient de souligner la section de l'extrait de Douglas Crockford

... Le second formulaire doit être utilisé avec des objets ...

Si vous avez besoin d’un tableau associatif (hashtable/dictionary) dans lequel les clés sont nommées au lieu d’être indexées numériquement, vous devrez l’implémenter en tant qu’objet, par exemple. var myAssocArray = {key1: "value1", key2: "value2"...};.

Dans ce cas myAssocArray.length va arriver null (car cet objet n'a pas de propriété 'length'), et votre i < myAssocArray.length ne vous mènera pas très loin. En plus de la commodité, je m'attendrais à ce que les tableaux associatifs offrent des avantages en termes de performances, car les clés de tableau peuvent être des propriétés utiles (c'est-à-dire la propriété ou le nom d'un membre d'un tableau), ce qui vous évite de devoir parcourir plusieurs fois array en évaluant à plusieurs reprises si des instructions permettent de trouver l'entrée de tableau que vous recherchez.

Quoi qu'il en soit, merci également pour l'explication des messages d'erreur JSLint, je vais maintenant utiliser le contrôle 'isOwnProperty' lorsque j'interviens dans ma myriade de tableaux associatifs!

2
tomfumb

Cela signifie que vous devez filtrer les propriétés de evtListeners avec la méthode hasOwnProperty .

0
Fabien Ménager

Juste pour ajouter au sujet de for in/for/$. Each, j’ai ajouté un scénario de test jsperf permettant d’utiliser $ .each vs pour: http://jsperf.com/each-vs-for- dans/2

Différents navigateurs/versions le traitent différemment, mais il semble que $ .each et tout droit sorti sont les options les moins chères en termes de performances.

Si vous utilisez for pour parcourir un tableau/objet associatif, sachant ce que vous recherchez et ignorant tout le reste, utilisez $ .each si vous utilisez jQuery, ou juste pour in (puis une pause; une fois atteint ce que vous savez devrait être le dernier élément)

Si vous parcourez un tableau pour effectuer quelque chose avec chaque paire de clés qu'il contient, utilisez la méthode hasOwnProperty si vous n'utilisez pas jQuery, et utilisez $ .each si vous utilisez jQuery.

Utilisez toujours for(i=0;i<o.length;i++) si vous n'avez pas besoin d'un tableau associatif, cependant ... lol chrome a été exécuté à 97% plus rapidement que pour for in ou $.each

0
Benno