创建一组按钮时,我遇到了一个奇怪的行为。首先编写它时,我希望每个按钮在单击时显示自己的编号,但显然情况并非如此。但奇怪的是,我仍然会得到两种不同的行为,具体取决于我如何定义应该显示的字符串。所以我的问题是:
innerHTML
分配内容时显示的结果不同?
function test(){
var foo = document.getElementById("foo")
var bar = document.getElementById("bar")
for(var k=0; k < 2; k++){
// 1st example
var a = document.createElement("button")
a.innerHTML = "A button "+k
var str = "click " + k
a.onclick = function(){alert(str)}
foo.appendChild(a)
// 2nd example
var b = document.createElement("button")
b.innerHTML = "B button "+k
b.onclick = function(){alert("click " + k)}
bar.appendChild(b)
}
}
test()
<div id="foo">
</div>
<div id="bar">
</div>
答案 0 :(得分:2)
这是一个常见问题。所发生的是,所有按钮只有一个var k
(以及一个str
),结果反映了这一点。在A案例中,str
更新的最后一次是k==1
,并且显示了k
。在B情况下,当循环停止时,function makeOnClick(k) {
var msg = "click " + k;
return function() {
alert(msg);
};
}
function test() {
var foo = document.getElementById("foo")
for (var k = 0; k < 2; k++) {
var a = document.createElement("button");
a.innerHTML = "A button " + k;
a.onclick = makeOnClick(k);
foo.appendChild(a)
}
}
test();
变为2,因此显示。
解决这个问题的一种方法是以下方法,因为每次调用函数时都会创建一组新的变量。
<div id="foo">
</div>
function makeButton(k) {
var button = document.createElement("button");
button.innerHTML = "A button " + k;
button.onclick = function() {
alert("Click " + k);
};
return button;
}
function test() {
var foo = document.getElementById("foo")
for (var k = 0; k < 2; k++)
foo.appendChild(makeButton(k));
}
test();
或者,您可以将按钮的整个创建拉到单独的函数中,这也可以起作用:
<div id="foo">
</div>
{{1}}
底线 - 你需要每个按钮至少有一个函数调用,所以按钮有自己的一组变量..
答案 1 :(得分:1)
您需要阅读有关JavaScript闭包的内容。基本上,您的k
变量是在单击回调之外定义的。因此,当您单击按钮时,无论哪一个,k
将始终等于它在for循环中的最后一个值(k = 1)。
获得所需结果的一种方法是将变量封装在函数中,如下所示:
function test(){
var foo = document.getElementById("foo")
var bar = document.getElementById("bar")
for(var k=0; k < 2; k++){
// 1st example
var a = document.createElement("button")
a.innerHTML = "A button "+k
a.onclick = (function(num){
return function () {
alert("click " + num);
}
})(k);
foo.appendChild(a)
// 2nd example
var b = document.createElement("button")
b.innerHTML = "B button "+k
b.onclick = (function(num){
return function () {
alert("click " + num);
}
})(k);
bar.appendChild(b);
}
}
test()
<div id="foo">
</div>
<div id="bar">
</div>
答案 2 :(得分:1)
Javascript有一项名为 hoisting 的功能。无论您在函数中声明var,声明都是提升或拉到函数的开头。
此外,vars不是块范围的。因此,循环中的变量将被拉到循环函数外部的开头,并且您只有一个版本,而不是每次迭代中的多个版本。
然后这个单个变量被附加到按钮的clicklistener的函数内的闭包引用。由于这个clicklistener在循环结束后被称为,因此var的值将是最后一次迭代的最后一次。
如果您有机会做出一些架构决策,您可以考虑使用Es 2015,其中引入了新的关键字let
。 let
是块范围的,并且还可以防止多次声明同一个变量。所以这种行为更像是人们所期望的,特别是当你习惯了其他语言时。事实上,最好不要在Es 2015中使用var
。
答案 3 :(得分:1)
使用let代替var
的另一种解决方案更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
function test(){
var foo = document.getElementById("foo")
var bar = document.getElementById("bar")
for(let k=0; k < 2; k++){
// 1st example
let a = document.createElement("button")
a.innerHTML = "A button "+k
let str = "click " + k
a.onclick = function(){alert(str)}
foo.appendChild(a)
// 2nd example
let b = document.createElement("button")
b.innerHTML = "B button "+k
b.onclick = function(){alert("click " + k)}
bar.appendChild(b)
}
}
test()
&#13;
<div id="foo">
</div>
<div id="bar">
</div>
&#13;