为什么循环将最后一个索引元素的引用分配给?

我想添加一个事件监听器到我所有的标签,当even被触发时, 每个标签都传递一个引用作为参数。 这是我写的function:

function validateDigitsFeature() { // Add the event listeners to input tags // Get the array of input tags var inputTags = document.getElementsByClassName('validateInput'); var tagId; // Loop through them, adding the onkeypress event listener to each one for (var i = 0; i < inputTags.length; i++) { // Give each input element an id tagId = inputTags[i].id = 'input_id_' + i; inputTags[i].addEventListener('keyup', function(){isNumberOrDot(event, tagId);}, false); } } 

基本上function应该做到以下几点:

  1. 将所有具有指定类名的input标签存储在一个数组中
  2. 循环访问数组,为每个标签添加一个id
  3. 使用isNumberOrDot(event, tagId)处理程序添加onkeyup事件侦听器。

问题

onkeyup事件被添加,但是它们的处理程序总是引用数组最后一个元素的tagId

代码/逻辑有什么问题? 而且如何解决?

注意

当然,这个问题与循环中的JavaScript Closure有关,而这个问题可能会有一个更为一般的答案,它是特定于正在使用的事件监听器的。 对于更高级的开发人员来说,将这个通用的解决scheme应用于这个问题可能很容易。 但对我来说,其他解决scheme仍然没有提供完整的解释,甚至没有工作。

先谢谢你。

因为在for循环已经完成运行之后,实际的事件会在将来某个时候发生,因此它的索引是最后一个值,并且像tagId这样的函数中的任何局部variables也都是最后一个值。 你需要创build一些闭包来保存每个事件处理程序唯一的itagId的值,这样它们都可以访问自己的值。

有几种不同的方法可以做到这一点,但都包括将i值传递给每个事件处理函数的函数。

这是一个使用IIFE(立即调用的函数expression式):

 function validateDigitsFeature() { // Add the event listeners to input tags // Get the array of input tags var inputTags = document.getElementsByClassName('validateInput'); // Loop through them, adding the onkeypress event listener to each one for (var i = 0; i < inputTags.length; i++) { // Give each input element an id (function() { // creates a unique function context for each event handler so the // value of tagId is unique for each event handler var tagId = inputTags[i].id = 'input_id_' + i; inputTags[i].addEventListener('keyup', function(){isNumberOrDot(event, tagId);}, false); })(); } } 

一个更常见的方法是将这个索引从for循环传入到闭包中,并在事件处理函数中根据它进行任何计算(尽pipe任何方法都可以正常工作),如下所示:

 function validateDigitsFeature() { // Add the event listeners to input tags // Get the array of input tags var inputTags = document.getElementsByClassName('validateInput'); // Loop through them, adding the onkeypress event listener to each one for (var i = 0; i < inputTags.length; i++) { // Give each input element an id (function(index) { // passes the `for` loop index into a function closure // so it is uniquely preserved for each event handler inputTags[index].addEventListener('keyup', function(){ isNumberOrDot(event, inputTags[index].id = 'input_id_' + index); }, false); })(i); } }