为什么这两个JavaScript不等同?

时间:2010-07-20 06:22:36

标签: javascript firebug closures jquery

在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>

2 个答案:

答案 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循环迭代数组项,如下面的注释中指出@CMSFurther 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) });
    })();
}