我是一年级的学生,我一直在浏览Stack Overflow,并且已经阅读了很多有关对象问题(参考)的信息,但是我不知道解决问题的方法。
我制作了对象数组,并遍历它们,以div
,img
,name
之类的所有信息填充value
,到目前为止,在这里没有问题。
问题在于用当前循环通过的对象填充mouseover函数(附加到图像),因此稍后当我将鼠标悬停在图像上时,该特定对象的所有信息都会显示在另一个{{ 1}}。
div
在函数for (i = 0; i < arrgezelschap.lenght; i++) {
var x = arrgezelschap[i];
var element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
element.addEventListener('mouseover', function() {
showinfo(x)
});
inhoud.append(element);
}
中,输出始终是数组的最后一个对象。
这是为什么,我需要怎么做才能将其保存或指向当前在函数中循环通过的对象?
答案 0 :(得分:2)
TL; DR:将var x
更改为let x
我真的无法比Creating closures in loops: A common mistake做得更好,但是我会尝试重新措辞。
比较这两个摘要的输出(如下)。唯一的区别是var
与let
。该示例演示了如何循环创建5个函数,但尚未调用它们。每个函数都引用在循环内,循环外和for
内声明的变量。然后,在循环结束时,我们调用所有函数以查看得到的结果。
在第一种情况下,变量outside
,i
(循环变量)和inside
(在循环内部声明)都用var
声明。在循环的每次迭代中,它们都是相同的变量。内部var提升到示波器的顶部(循环外部)。
调用我们创建的所有函数时,我们将看到它们都引用每个变量的唯一实例,并且它们都具有循环完成后变量具有的值。
let functions = [];
var outside = 0;
for (var i = 0; i < 5; ++i) {
outside = i * 10;
var inside = i * 100;
functions.push(() => { console.log(outside, i, inside); })
}
functions.map(f => f()); // call all the functions
输出:
40 5 400
40 5 400
40 5 400
40 5 400
40 5 400
在第二个示例中,所有变量都用let
声明。 i
中声明的变量for
和循环体内声明的变量inside
在循环的每次迭代中都是不同变量。但是outside
变量是在循环外部声明的,因此在循环的每次迭代中仍然只使用一个outside
变量。
当我们调用这次创建的所有函数时,我们看到每个函数都显示一个不同的变量i
和inside
,它们的值是它们在循环的特定迭代过程中所持有的值,因为变量仅在该循环的迭代中存在,并且函数已绑定到该迭代所使用的变量的实例。但是outside
变量在每次迭代时都是相同的变量,并且仅包含一个值:它在循环结束时具有的值。
let functions = [];
let outside = 0;
for (let i = 0; i < 5; ++i) {
outside = i * 10;
let inside = i * 100;
functions.push(() => { console.log(outside, i, inside); })
}
functions.map(f => f()); // call all the functions
输出:
40 0 0
40 1 100
40 2 200
40 3 300
40 4 400
在您的情况下,每个函数都绑定到同一(唯一)变量x
。如果将x
的声明从var x
更改为let x
,那么对于每次循环迭代,您将获得一个不同的变量x
,并且事件监听器函数将被绑定每次都更改为不同的x
,该值将具有对应于该循环迭代的值。
脚注:希望functions.map(f => f());
不会让您感到困惑。它只是调用数组中的所有函数。与此相同:
for (var index = 0; index < functions.length; ++index) {
functions[index]();
}
答案 1 :(得分:1)
这是因为x是此处的引用,而不是值,并且在循环时会更改。看看这个:
let x = 0;
let fcn = a => console.log(a);
function execAnotherFcn(fcn) {
fcn(x);
}
execAnotherFcn(fcn);
x++;
execAnotherFcn(fcn);
答案 2 :(得分:0)
@PopHips答案解释了出问题的理论。因此,这是您的代码的一个有效示例,因此您可以遵循它。
for(i =0;i<arrgezelschap.lenght;i++){
var x = arrgezelschap[i];
var element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
element.dataset.identifyer = i;
element.addEventListener('mouseover', function(e) {
showinfo(arrgezelschap[e.target.dataset.identifyer])
});
inhoud.append(element);
}
因此,因为我们使用的是事件列表器,它将提供EventArgs对象,该对象包含一个称为target的属性,该属性是受影响的HTMLElement。我们可以使用数据集(data-
)系统将标识符保存到对象的数据集中,以便在事件处理程序中使用它。
请注意,不应按原样使用此答案,此答案有一些非常不好的做法,永远不要在生产代码中创建函数。
答案 3 :(得分:0)
您可以使用dataset
属性来存储您的信息。
这是我的实现方式
const root = document.querySelector('#root');
function createImagePlaceholder(color, data) {
const el = document.createElement('div');
el.style.width = '50px';
el.style.height = '50px';
el.style.margin = '5px';
el.style.backgroundColor = color;
el.dataset = data;
root.appendChild(el);
el.addEventListener('mouseover', () => {
document.querySelector('pre').innerText = JSON.stringify(data);
});
el.addEventListener('mouseleave', () => {
document.querySelector('pre').innerText = '';
});
}
createImagePlaceholder('red', { text: 'I am a red block' });
createImagePlaceholder('blue', { text: 'I am a blue block' });
<div id="root"></div>
<pre><pre>
答案 4 :(得分:0)
您可以通过确定元素块级别的范围来解决此问题。 发生这种情况是因为x的值是作为闭包发送的,而var则定义为函数级别。事件列表器函数将在以后的时间执行(而不是在主线程中执行),因此x的值为循环更改为最后一个值。 可以使用let关键字或IIFE来完成。
1。
for (i = 0; i < arrgezelschap.length; i++) {
let x = arrgezelschap[i];
let element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
element.addEventListener('mouseover', function() {
showinfo(x)
});
inhoud.append(element);
}
2。
for (i = 0; i < arrgezelschap.lenght; i++) {
var x = arrgezelschap[i];
var element = document.createElement("img");
element.src = x.artikelFoto + "k.jpg";
(function(x){element.addEventListener('mouseover', function() {
showinfo(x)
});})(x);
inhoud.append(element);
}