Странные вещи в JavaScript "для" - PullRequest
8 голосов
/ 30 июня 2010

Я использую jQuery, и у меня есть странная вещь, которую я не понимаю.У меня есть некоторый код:

for (i = 1; i <= some_number; i++) {
    $("#some_button" + i).click(function() {
        alert(i);
    });
}

"# some_button", как следует из названия, - это несколько кнопок.При нажатии на них должно появиться окно с номером, верно?Но они этого не делают.Если есть 4 кнопки, они всегда выскакивают «5» (количество кнопок + 1).Почему это так?

Ответы [ 6 ]

18 голосов
/ 30 июня 2010

Это связано с областью JavaScript. Вы можете легко обойти это, введя другую область видимости, добавив функцию и заставив эту функцию вызвать саму себя и передать i:

for (var i = 1; i <= some_number; i++) {
  (function(j) {
    $("#some_button" + j).click(function() {
      alert(j);
    });
  })(i);
}

Это создает замыкание - когда внутренняя функция имеет доступ к области, которая больше не существует при вызове функции. См. эту статью на MDC для получения дополнительной информации.

РЕДАКТИРОВАТЬ: RE: самовозвратные функции: самовозвратная функция является функцией, которая вызывает себя анонимно Вы не создаете его экземпляр и не присваиваете его переменной. Он принимает следующую форму (обратите внимание на вводные парены):

(function(args) {
  // function body that might modify args
})(args_to_pass_in);

Относительно этого вопроса, тело анонимной функции будет:

$("#some_button" + j).click(function() {
  alert(j);
});

Объединяя их вместе, мы получаем ответ в первом блоке кода. Функция анонимного самовызова ожидает аргумент с именем j. Он ищет любой элемент с идентификатором some_button с целочисленным значением j в конце (например, some_button1, some_button10). Каждый раз, когда щелкает один из этих элементов, он оповещает о значении j. От второй до последней строки решения передается значение i, которое является счетчиком цикла, в котором вызывается анонимная функция самовызова. Сделано иначе, это может выглядеть так:

var innerFunction = function(j) {
  $("#some_button" + j).click(function() {
    alert(j);
  });
};

for (var i = 1; i <= some_number; i++) {
  innerFunction(i);
}
9 голосов
/ 30 июня 2010

У вас очень распространенная проблема закрытия в цикле for.

Переменные, заключенные в замыкание, совместно используют одну и ту же среду, поэтому к тому времени, когда будет вызван обратный вызов click, цикл будет работать, а переменная i будет указывать на последнюю запись. *

Вы можете решить эту проблему с помощью еще большего числа замыканий, используя фабрику функций:

function makeOnClickCallback(i) {  
   return function() {  
      alert(i);
   };  
} 

var i;

for (i = 0; i < some_number; i++) {
    $("#some_button" + i).click(makeOnClickCallback(i));
}

Это может быть довольно сложной темой, если вы не знакомы с тем, как работают замыкания. Вы можете ознакомиться со следующей статьей Mozilla для краткого введения:

2 голосов
/ 30 июня 2010

Потому что в тот момент, когда вы нажимаете на них, я == 5.

0 голосов
/ 30 июня 2010

Это очень умный код. Настолько умный, это вопрос о ТАК. :) Я бы вообще обошел этот вопрос, замолчав код, просто чтобы иметь возможность понять его (или сделать так, чтобы его понял коллега) через шесть месяцев. Замыкания имеют свое место, но в этом случае я бы избегал их в пользу более понятного кода.

Возможно, я бы прикрепил одну и ту же функцию ко всем кнопкам, которая бы получала кнопку из события, убрала "some_button" из идентификатора и предупредила результат. Не так красиво, но я гарантирую, что все в офисе могли сразу за этим следить.

0 голосов
/ 30 июня 2010

Это из-за того, как замыкания работают в JavaScript.Каждая из 5 создаваемых вами функций, по сути, использует одну и ту же переменную i.Значение i внутри вашей функции не оценивается при создании функции, но когда происходит событие щелчка, к этому времени значение i равно 5.

Существуют различные способыобойти это (когда это поведение не то, что вы хотите).Одна (если у вас есть простая функция, как у вас здесь) - это использовать конструктор Function вместо литерала функции:

$("#some_button" + i).click(new Function("alert("+i+")");
0 голосов
/ 30 июня 2010
  (function (some_number) {
    for (i = 1; i <= some_number; i++) {
      $("#some_button" + i).click(function() {
        alert(i);
      });
    }
  })(some_number);

Оберните функцию снаружи, потому что для скорости и факта я буду продолжать сбрасывать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...