Почему эти два бита JavaScript не эквивалентны? - PullRequest
3 голосов
/ 20 июля 2010

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

12 голосов
/ 20 июля 2010

У вас будет очень распространенная проблема замыкания в цикле 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.

2 голосов
/ 20 июля 2010

Это происходит потому, что при выполнении кода анонимной функции 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) });
    })();
}
...