在jquery 1.4.2中,ff 3.6.6:
以下代码生成三个div,它们会像您期望的那样将消息写入firebug控制台。但是,如果您取消注释掉循环并注释掉手动执行它的3行,则它不起作用 - 将鼠标悬停在任何div上会导致"three"
被写入控制台。
为什么这两种方法彼此不同?在每一个中,您使用选择器来查找元素并向其添加事件。
<head>
<script type="text/javascript" src="/media/js/jquery.js"></script>
<script>
$( document ).ready( function() {
$("#one").mouseenter(function(){console.log("one")})
$("#two").mouseenter(function(){console.log("two")})
$("#three").mouseenter(function(){console.log("three")})
// names=['one','two','three'];
// for (k in names){
// id=names[k]
// $("#"+id).mouseenter(function(){console.log(id)})
// }
})
</script>
</head>
<body>
<span id="one">ONE</span>
<p><span id="two">TWO</span></p>
<p><span id="three">THREE</span></p>
</body>
答案 0 :(得分:12)
您将在for in
循环中获得a very common closure problem。
封闭在闭包中的变量共享相同的单个环境,因此在调用mouseenter
回调时,循环将运行并且id
变量将指向该值names
数组的最后一个元素。
如果你不熟悉闭包的工作方式,这可能是一个非常棘手的话题。您可能需要查看以下文章以获得简要介绍:
你可以使用函数工厂解决这个问题:
function makeMouseEnterCallback (id) {
return function() {
console.log(id);
};
}
// ...
var id, k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
id = names[k];
$("#" + id).mouseenter(makeMouseEnterCallback(id));
}
您还可以按如下方式内联上述函数工厂:
var id, k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
id = names[k];
$("#" + id).mouseenter((function (p_id) {
return function() {
console.log(p_id);
};
})(id));
}
任何另一个解决方案可以是@d.m suggested in another answer,将每个迭代包含在自己的范围内:
var k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
(function() {
var id = names[k];
$("#" + id).mouseenter(function () { console.log(id) });
})();
}
虽然与此问题无关,但通常建议避免使用for in
循环迭代数组项,如下面的注释中指出@CMS(Further reading })。此外,使用分号显式终止语句也被认为是JavaScript中的一种很好的做法。
答案 1 :(得分:2)
这是因为当执行匿名函数function(){console.log(id)}
的代码时,id
的值确实是three
。
要使用以下技巧将每个循环迭代的值括起来放入匿名回调范围:
names=['one', 'two', 'three'];
for (var k in names) {
(function() {
var id = names[k];
$("#"+id).mouseenter(function(){ console.log(id) });
})();
}