你这里的问题是,可变i
不会有你认为它会具有的价值。这是由于关闭如何工作。
试试这个简单的例子:
for(var i = 0; i < 5; i++) {
console.log("Before timeout: " + i);
setTimeout(function() {
console.log("After timeout: " + i);
}, 1000);
}
当您运行此,你会期待什么?可能在一秒钟后,你在控制台中看到了这一点?
Before timeout: 0
Before timeout: 1
Before timeout: 2
Before timeout: 3
Before timeout: 4
After timeout: 0
After timeout: 1
After timeout: 2
After timeout: 3
After timeout: 4
但你猜怎么着,你会看到这一点:
Before timeout: 0
Before timeout: 1
Before timeout: 2
Before timeout: 3
Before timeout: 4
After timeout: 5
After timeout: 5
After timeout: 5
After timeout: 5
After timeout: 5
为什么5? for
声明基本上将i
设置为0,然后在每次迭代中对其进行计数并每次检查它是否仍低于5,如果不是,则打破循环。因此,在循环后,i
有值为5,这样的:
...给:
After loop: 5
好,但为什么使用值i
在循环之后有?这是因为您传递给setTimeout
的函数回调直接访问外部范围中的i
变量!而且因为函数只会在一秒后运行(在循环完成之后),此时i
的值将为5,如上所示!
因此,解决办法是创建这个变量,它是本地的每次迭代的本地副本。现在,这是不是因为它似乎是因为在JavaScript中,像for
控制块不会创建范围(除非你使用ES6和let
)微不足道。你必须使用另一个匿名函数围绕它来创建一个本地副本,每次它是那么的不同:
for(var i = 0; i < 5; i++) {
// This is a so-called "immediately executed function expression"
// It's an anonymous function which is then immediately called due
// to the `()` at the end.
(function() {
var j = i; // Now we have a local `j`
console.log("Before timeout: " + j);
setTimeout(function() {
console.log("After timeout: " + j);
}, 1000);
})();
}
另一种方式由于这一点,这是一个有点短,是这一招:
for(var i = 0; i < 5; i++) {
// See how this anonymous function now takes a parameter `i`?
// And we pass this parameter when calling this function below,
// which basically creates an inner copy which we can now also call
// `i`.
(function(i) {
console.log("Before timeout: " + i);
setTimeout(function() {
console.log("After timeout: " + i);
}, 1000);
})(i);
}
这就是为什么我建议在迭代数组时使用类似forEach
的东西,因为它们将函数回调作为参数,所以您自动在代码中有一个本地作用域。
现在你的情况,你可以使用上面的一个匿名函数解决方案,或者类似forEach
,但有一个问题:element
变量不是普通的JavaScript数组,它是一个DOM元素列表,它没有forEach
。但是,你仍然可以使它工作使用Array.prototype.forEach.call
招:
function recordData(){
var element = document.getElementsByClassName("bubble");
Array.prototype.forEach.call(element, function(i) {
element[i].addEventListener("click", function(){
var id = element[i].attributes.id.value;
var x_cordinate = element[i].children[2].attributes.x.value;
var y_cordinate = element[i].children[2].attributes.y.value;
var keyword = element[i].children[0].textContent;
clicked_elements.push({
id: id,
x_cordinate: x_cordinate,
y_cordinate: y_cordinate,
keyword: keyword
})
}, false);
});
}
之前,因为你有效利用element[element.length].attributes
(因为i
已经是一个超出你的数组上限的),没有工作,所以element[i]
是未定义。
你走了!
但是,更好的方法是使用this
,因为在事件侦听器中,this
引用事件的目标。因此,而不是element[i]
,您可以在听众中随处使用this
!
您可以使用'this'来引用事件侦听器函数中的clicked元素。你会陷入麻烦,因为在你的for循环中'i'正在改变。 – user3297291
你可以通过使用IIFE来解决这个问题(var i = 0; i
jcubic
参考这项工作!谢谢 ! –