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.
这是什么意思?
首先,不要使用
The reason behind this is the following: each object in JavaScript has a special field called
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] |
这是扩展对象和添加新方法的标准方法。很多图书馆都这样做。不过,让我们看看
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是另一个数组索引。当然,它不是真正的数字索引,但
幸运的是,所有对象都有一个
1 2 3 4 5 6 7 8 9 | for (o in listeners) { if (listeners.hasOwnProperty(o)) { console.log(o); } } //prints: // 0 // 1 // 2 |
请注意,尽管此代码在数组中按预期工作,但决不能对数组使用
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,那么
1 2 3 | $.each(evtListeners, function(index, elem) { // your code }); |
@所有-javascript中的所有内容都是一个对象(),所以类似"只在对象上使用"这样的语句有点误导性。此外,javascript不是强类型的,因此1=="1"是正确的(尽管1=="1"不是,但crockford在这方面很重要)。当涉及到JS中数组的编程概念时,类型在定义中很重要。
@Brenton-无需成为术语独裁者;"关联数组"、"字典"、"哈希"、"对象",这些编程概念都适用于JS中的一个结构。它是名称(键、索引)值对,其中值可以是任何其他对象(字符串也是对象)。
所以,
1 | var myarray = []; |
创建一个数组结构,该数组的限制是所有索引(aka键)必须是整数。它还允许通过.push()自动分配新索引
1 | var myarray = ["one","two","three"]; |
确实最好通过
但如何:
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),其中键是命名的,而不是数字索引的,那么您必须将它作为一个对象来实现,例如
在这种情况下,
无论如何,还要感谢对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。
但是,如果不需要关联数组,请始终使用
这意味着您应该使用hasownProperty方法过滤evtlisteners的属性。