JSLint错误“ for in的主体应包含在if语句中”是什么意思?


242

我在我的JavaScript文件上使用了JSLint。它引发了错误:

for( ind in evtListeners ) {

第41行的字符9中的问题:for in的主体应包装在if语句中,以从原型中过滤掉不需要的属性。

这是什么意思?


5
默认情况下,“ in”也会迭代继承的属性。通常,将主体包裹起来if (evtListeners.hasOwnProperty(ind))以将处理限制为仅拥有(非继承)属性。仍然,在某些情况下,您确实想遍历所有属性,包括继承的属性。在这种情况下,JSLint会强制您将循环体包装在if语句中,以确定您真正想要的属性。这将使JSlint满意: if (evtListeners[ind] !== undefined)
xorcus '16

1
大多数答案已过时。更新的解决方案可以在stackoverflow.com/a/10167931/3138375
eli-bd

Answers:


430

首先,切勿使用for in循环枚举数组。决不。用好旧的for(var i = 0; i<arr.length; i++)

其原因如下:JavaScript中的每个对象都有一个名为的特殊字段prototype。您添加到该字段的所有内容都将可以在该类型的每个对象上访问。假设您希望所有数组都有一个很酷的新函数filter_0,该函数将滤除零。

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]

这是扩展对象和添加新方法的标准方法。许多图书馆都这样做。但是,让我们看看for in现在如何工作:

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

你有看到?突然认为filter_0是另一个数组索引。当然,它实际上不是数字索引,而是for in通过对象字段枚举,而不仅仅是数字索引。因此,我们现在枚举每个数字索引 filter_0。但是filter_0不是任何特定数组对象的字段,现在每个数组对象都具有此属性。

幸运的是,所有对象都有一个hasOwnProperty方法,该方法检查此字段是否真正属于该对象本身,或者它是否简单地从原型链继承而属于该类型的所有对象。

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

请注意,尽管此代码对数组可以按预期工作,但您绝对不能对数组使用for infor each in。请记住,它for in枚举对象的字段,而不是数组索引或值。

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

43
您不应该使用for in迭代数组的方法,因为该语言不会保证for in数组枚举的顺序。它可能不是数字顺序的。另外,如果使用`for(i = 0; i <array.length; i ++)样式结构,则可以确保按顺序迭代数字索引,而没有字母数字属性。
布列塔尼

谢谢!我将其保存为参考。
nyuszika7h 2011年

我发现自己又在看这个答案,因为我确信JSLint的这一部分已损坏。我的代码大致是:for(o in listeners){if(listeners.hasOwnProperty(i)){console.log(o); 问题是我有一个错误,我将变量名i切换为o并错过了引用。JSLint非常聪明,可以确保您在检查hasOwnProperty上是否存在正确对象上的正确属性。
提请

12
但是,因为in可以迭代对象的属性。OP从来没有说过for应用于数组。hasOwnProperty是最佳实践,但是在某些情况下您可能不想要它-例如,如果某个对象扩展了另一个对象,并且您希望同时列出对象和“父”对象的属性。
gotofritz 2012年

3
我认为,for-in与其让人们远离循环(顺便说一句,这是很棒的),不如让我们教育他们如何工作(在此答案中做得正确)并介绍给他们,Object.defineProperty()以便他们可以安全地扩展其原型而不会破坏任何东西。顺便说一句,扩展原生对象的原型应该没有Object.defineProperty
罗伯特·罗斯曼

87

jslint的作者道格拉斯·克罗克福德(Douglas Crockford)对此问题已经写过很多次(并发表过演讲)。有一个章节页他的网站的覆盖这样的:

声明

for类的语句应具有以下形式:

for (initialization; condition; update) {
    statements
}

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

第一种形式应与数组和具有预定迭代次数的循环一起使用。

第二种形式应与对象一起使用。请注意,添加到对象原型的成员将包含在枚举中。通过使用hasOwnProperty方法区分对象的真实成员,以进行防御性编程是明智的:

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

克罗克福德(Crockford)在YUI剧院也有一个视频系列,他在这方面谈到了这一点。Crockford的有关javascript的视频/讲座系列是您必须认真对待javascript的必经之路。


21

错误:(jsHint会引发错误)

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

好:

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

8

Vava的答案已成定局。如果使用jQuery,则该$.each()函数会处理此问题,因此使用起来更安全。

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

5
如果在这里考虑性能,如果您可以避免使用原始循环,则不建议使用$.each(或underscore.js的_.eachfor。jsperf有一些值得一看的比较测试
nickb

3
这个(jsperf.com/each-vs-each-vs-for-in/3)更现实,因为它使用了基本的原型过滤器
dvdrtrgn

7

@all-JavaScript中的所有内容都是一个对象(),因此诸如“仅在对象上使用此对象”之类的语句有点误导。另外,JavaScript不是强类型的,因此1 ==“ 1”为true(尽管1 ===“ 1”不是,Crockford对此很重视)。当涉及到JS中数组的流行学概念时,在定义中键入很重要。

@Brenton-无需成为术语独裁者;“关联数组”,“字典”,“哈希”,“对象”,这些编程概念都适用于JS中的一种结构。它是名称(键,索引)值对,其中值可以是任何其他对象(字符串也是对象)

因此, new Array()[]

new Object() 大致类似于 {}

var myarray = [];

创建一个结构,该结构是一个数组,其限制为所有索引(即键)必须为整数。它还允许通过.push()自动分配新索引

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

确实最好通过以下方式处理 for(initialization;condition;update){

但是关于:

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

试试这个:

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

也许不是数组的最佳用法,而只是说明事情并不总是很明确。

如果您知道自己的键,并且绝对可以确定它们不是整数,则唯一的类似于结构的数组选项就是对象。

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

“仅在对象上使用此对象”意味着不要在数组或任何其他扩展对象的对象上使用它,否则,正如您所指出的那样,因为一切都扩展了对象,这将是非常愚蠢的
Juan Mendes 2012年

““关联数组”,“字典”,“哈希”,“对象”,这些编程概念都适用于JS中的一种结构。否。它们是来自不同语言的不同概念,彼此相似。但是,如果您假设它们/完全相同/并以相同的方式使用,并且出于相同的目的,那么您将犯下一些非常愚蠢的错误,而这可以通过减少对语言的不了解来避免。您正在使用作品。
布列塔尼

2

当然,这有点极端

...切勿使用for in循环枚举数组。决不。使用旧的for(var i = 0; i <arr.length; i ++)

值得强调一下道格拉斯·克罗克福德摘录中的部分

...第二种形式应与对象一起使用...

如果您需要一个关联数组(又名哈希表/字典),其中键是命名的,而不是数字索引的,则必须将其实现为一个对象,例如var myAssocArray = {key1: "value1", key2: "value2"...};

在这种情况下,myAssocArray.length它将变为null(因为此对象没有'length'属性),并且您i < myAssocArray.length不会走得太远。除了提供更大的便利之外,我还希望关联数组在许多情况下都可以提供性能优势,因为数组键可以是有用的属性(即数组成员的ID属性或名称),这意味着您不必冗长地迭代数组反复评估if语句以查找所需的数组条目。

无论如何,也感谢您对JSLint错误消息的解释,在遍历无数个关联数组时,我现在将使用'isOwnProperty'检查!


1
你很困惑。javascript中没有“关联数组”之类的东西。严格来说,这是一个php概念。
布列塔尼

的确,这些对象没有length属性,但是您可以使用另一种方式:var myArr = []; myArr['key1'] = 'hello'; myArr['key2'] = 'world';
nyuszika7h 2011年

3
@ Nyuszika7H那是错误的方式。如果不需要整数索引的Array,则不应使用var myArr = [],它应该var myArr = {}在PHP中是同一回事,而在JS中则不然。
Juan Mendes

关联的“数组”不是数组。
文森特·麦克纳布


0

只是为了添加for / for / $。each主题,我添加了一个使用$ .each vs for in的jsperf测试用例:http ://jsperf.com/each-vs-for-in/2

不同的浏览器/版本对它的处理方式有所不同,但$ .each和in似乎是性能上最便宜的选择。

如果您使用for in遍历关联数组/对象,知道要做什么并忽略其他所有内容,请使用$ .each(如果您使用jQuery),或者只使用in(然后是一个中断;一旦完成,达到您所知道的应该是最后一个元素)

如果要遍历数组以对其中的每个键对执行某些操作,如果不使用jQuery,则应使用hasOwnProperty方法,如果要使用jQuery,则应使用$ .each。

不过,for(i=0;i<o.length;i++)如果不需要关联数组,请务必使用...大声笑chrome的执行速度比for in或in快97%$.each

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.