为什么 JavaScript 的这两部分不等价?

发布于 2024-09-10 21:51:25 字数 922 浏览 4 评论 0原文

在 jquery 1.4.2、ff 3.6.6 中:

以下代码生成三个 div,它们按照您的预期将消息写入 firebug 控制台。但是,如果您取消注释掉循环并注释掉手动执行的 3 行,则它将不起作用 - 将鼠标悬停在任何 div 上都会导致 "third" 被写入控制台。

为什么这两种方法有什么不同?在每个元素中,您都使用选择器来查找元素并向其添加事件。

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

in jquery 1.4.2, ff 3.6.6:

The following code produces three divs, which write messages to the firebug console as you would expect. However, if you uncomment out the loop and comment out the 3 lines doing it manually, it doesn't work - mousing over any of the divs results in "three" being written to the console.

Why are these two methods any different than each other? In each one you use a selector to find the element and add an event to it.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

意中人 2024-09-17 21:51:25

您将在 一个非常常见的闭包问题代码>for in循环。

闭包中包含的变量共享相同的单一环境,因此当调用 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));
}

任何其他解决方案都可以是 @dm 在另一个答案中建议,将每个迭代包含在自己的范围内:

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 在下面的评论中指出(进一步阅读)。此外,在 JavaScript 中,用分号显式终止语句也被认为是一种很好的做法。

You would be having a very common closure problem in the for in loop.

Variables enclosed in a closure share the same single environment, so by the time the mouseenter callback is called, the loop will have run its course and the id variable will be left pointing to the value of the last element of the names array.

This can be quite a tricky topic, if you are not familiar with how closures work. You may want to check out the following article for a brief introduction:

You could solve this with even more closures, using a function factory:

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));
}

You could also inline the above function factory as follows:

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));
}

Any yet another solution could be as @d.m suggested in another answer, enclosing each iteration in its own scope:

var k, 
    names = ['one','two','three'];

for (k = 0; k < names.length; k++) {
  (function() {
    var id = names[k];
    $("#" + id).mouseenter(function () { console.log(id) });
  })();
}

Although not related to this problem, it is generally recommended to avoid using a for in loop to iterate over the items of an array, as @CMS pointed out in a comment below (Further reading). In addition, explicitly terminating your statements with a semicolon is also considered a good practice in JavaScript.

々眼睛长脚气 2024-09-17 21:51:25

出现这种情况是因为执行匿名函数 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) });
    })();
}

This happens because when the code of the anonymous function function(){console.log(id)} is executed, the value of id is three indeed.

To can use the following trick to enclose the value on each loop iteration into the anonymous callback scope:

names=['one', 'two', 'three'];
for (var k in names) {
    (function() {
        var id = names[k];
        $("#"+id).mouseenter(function(){ console.log(id) });
    })();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文