关于javascript:JSLint错误’for的主体应该包含在if语句中’是什么意思?

What does the JSLint error 'body of a for in should be wrapped in an if statement' mean?

我用JSLINT在我的Javascript文件上。It threw the error:

1
for( ind in evtListeners ) {

Problem at line 41 character 9: The body of a for in should be
wrapped in an if statement to filter unwanted
properties from the prototype.

这是什么意思?


首先,不要使用for in循环枚举数组。从未。使用好的旧的for(var i = 0; i.

The reason behind this is the following: each object in JavaScript has a special field called prototype。添加到该字段中的所有内容都可以在该类型的每个对象上访问。假设您希望所有数组都有一个很酷的新函数,名为filter_0,它将过滤掉零。

1
2
3
4
5
6
7
8
9
10
11
12
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现在是如何工作的:

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

你明白了吗?它突然认为过滤器_0是另一个数组索引。当然,它不是真正的数字索引,但for in通过对象字段枚举,而不仅仅是数字索引。所以我们现在正在枚举每个数字索引和filter_0。但是filter_0不是任何特定数组对象的字段,现在每个数组对象都有这个属性。

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

1
2
3
4
5
6
7
8
9
for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}
 //prints:
 //  0
 //  1
 //  2

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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


JSlint的作者道格拉斯·克罗克福德(DouglasCrockford)曾多次就这个问题撰文(并发表过意见)。在他的网站的这个页面上有一个部分包括:

for Statement

A for class of statements should have
the following form:

1
2
3
4
5
6
7
8
9
for (initialization; condition; update) {
    statements
}

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

The first form should be used with
arrays and with loops of a
predeterminable number of iterations.

The second form should be used with
objects. Be aware that members that
are added to the prototype of the
object will be included in the
enumeration. It is wise to program
defensively by using the
hasOwnProperty method to distinguish
the true members of the object:

1
2
3
4
5
for (variable in object) {
    if (object.hasOwnProperty(variable)) {
        statements
    }
}

克罗克福德也有一个关于尤伊剧院的视频系列,他在那里谈论这个。Crockford的一系列关于javascript的视频/谈话是你必须要看的,看你是否对javascript有点认真。


错误:(JShint将引发错误)

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

好:

1
2
3
4
5
for (var name in item) {
  if (item.hasOwnProperty(name)) {
    console.log(item[name]);
  }
}

瓦娃的回答是正确的。如果使用jquery,那么$.each()函数会处理这一问题,因此使用起来更安全。

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


@所有-javascript中的所有内容都是一个对象(),所以类似"只在对象上使用"这样的语句有点误导性。此外,javascript不是强类型的,因此1=="1"是正确的(尽管1=="1"不是,但crockford在这方面很重要)。当涉及到JS中数组的编程概念时,类型在定义中很重要。

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

所以,new Array()[]相同

new Object(){}大致相似。

1
var myarray = [];

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

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

确实最好通过for(initialization;condition;update){处理。

但如何:

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

试试这个:

1
2
3
4
5
6
7
8
9
10
11
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]);
    }
}

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

如果您知道您的键,而且它们不是整数,那么您唯一的类似数组的结构选项就是对象。

1
2
3
4
5
6
7
8
9
10
11
var i, myarray= {
  "first":"john",
  "last":"doe",
   100:"foo",
   150:"baz"
};
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" :"+myarray[i]);
    }
}


说起来肯定有点极端

...never use a for in loop to
enumerate over an array. Never. Use
good old for(var i = 0;
i

值得一提的是道格拉斯·克罗克福德摘录中的部分。

...The second form should be used with
objects...

如果您需要一个关联数组(又称hashtable/dictionary),其中键是命名的,而不是数字索引的,那么您必须将它作为一个对象来实现,例如var myAssocArray = {key1:"value1", key2:"value2"...};

在这种情况下,myAssocArray.length将变为空(因为该对象没有"length"属性),而您的i < myAssocArray.length不会让您走得太远。除了提供更大的便利性之外,我还希望关联数组在许多情况下提供性能优势,因为数组键可以是有用的属性(即数组成员的ID属性或名称),这意味着您不必重复使用冗长的数组来计算语句是否要查找尾部的数组条目。呃。

无论如何,还要感谢对jslint错误消息的解释,我将在通过无数关联数组进行交互时立即使用"isownproperty"检查!


为了增加for-in/for/$的主题,我分别添加了一个jspef测试用例,用于使用$.each-vs-for-in:http://jspef.com/each-vs-for-in/2

不同的浏览器/版本对它的处理方式不同,但似乎是$。每个和直接输入都是性能方面最便宜的选项。

如果使用for-in迭代关联数组/对象,知道要搜索的是什么,而忽略了其他所有内容,请使用$。如果使用jquery,则每个都要使用,或者只使用for-in(然后是break;一旦达到所知道的应该是最后一个元素)

如果您正在迭代一个数组以对其中的每个键对执行某些操作,那么如果不使用jquery,则应使用hasownproperty方法;如果确实使用jquery,则应使用$.each。

但是,如果不需要关联数组,请始终使用for(i=0;i…lol chrome比a for in或$.each快97%。


这意味着您应该使用hasownProperty方法过滤evtlisteners的属性。