261

我在我的一个 JavaScript 文件上使用了JSLint。它抛出了错误:

for( ind in evtListeners ) {

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

这是什么意思?

4

9 回答 9

453

首先,永远不要使用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 inandfor 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
于 2009-12-26T11:17:44.223 回答
92

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 剧院也有一个视频系列,他在其中谈到了这一点。如果您对 javascript 有一点认真,Crockford 的一系列视频/讲座是必看的。

于 2009-12-26T11:41:31.913 回答
21

不好:(jsHint会抛出错误)

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

好的:

for (var name in item) {
  if (item.hasOwnProperty(name)) {
    console.log(item[name]);
  }
}
于 2015-09-10T11:09:55.573 回答
8

Vava 的回答是正确的。如果您使用 jQuery,则该$.each()函数会处理此问题,因此使用起来更安全。

$.each(evtListeners, function(index, elem) {
    // your code
});
于 2010-01-30T06:32:34.787 回答
7

@all - JavaScript 中的一切都是一个对象 (),所以像“只在对象上使用 this”这样的声明有点误导。此外,JavaScript 不是强类型的,因此 1 == "1" 为真(尽管 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]);
    }
}
于 2010-12-10T19:41:31.420 回答
2

肯定说的有点极端

...永远不要使用 for in 循环来枚举数组。绝不。使用旧的 for(var i = 0; i<arr.length; i++)

?

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

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

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

在这种情况下myAssocArray.length会出现 null (因为这个对象没有“长度”属性),你i < myAssocArray.length不会让你走得太远。除了提供更大的便利之外,我希望关联数组在许多情况下提供性能优势,因为数组键可以是有用的属性(即数组成员的 ID 属性或名称),这意味着您不必遍历冗长的array 反复评估 if 语句以找到您所追求的数组条目。

无论如何,还要感谢对 JSLint 错误消息的解释,我现在将在通过无数关联数组进行交互时使用“isOwnProperty”检查!

于 2010-04-19T14:54:07.450 回答
1

老实说,添加一整行只是为了检查键是否存在,同时使用应该迭代对象键的语法是for .. in没有用的。改用就好Object.keys(obj).forEach(key => {}了。

于 2021-10-29T13:37:57.610 回答
0

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

于 2009-12-26T11:09:54.583 回答
0

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

不同的浏览器/版本处理它的方式不同,但似乎 $.each 和直接用于在性能方面是最便宜的选择。

如果您使用 for in 遍历关联数组/对象,知道您在做什么并忽略其他所有内容,则使用 $.each 如果您使用 jQuery,或者只是 for in (然后休息;一旦你已经达到了你所知道的应该是最后一个元素)

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

for(i=0;i<o.length;i++)如果您不需要关联数组,请始终使用...lol chrome 的执行速度比 for in 或快 97%$.each

于 2011-05-20T05:40:24.390 回答