1. 用var失败的原因
var btns = document.getElementsByTagName('button');
for(var i = 0;i<btns.length;i++){
btns[i].onclick = function () {
alert(i);
}
}
点击监听事件是异步执行的,当点击按钮执行相应函数之前,同步任务(循环)已经执行结束,此时在全局作用域下的 i 已经为固定值。执行alert(i)时,沿着作用域链找到全局作用域下的 i ,导致点击每个按钮得到的结果相同。
2. 解决方法
2.1 添加属性
for(var i = 0;i<btns.length;i++){
btns[i].index = i;
btns[i].onclick = function () {
alert(this.index);
}
}
原理:给每一个按钮绑定一个单独的属性,之后在执行点击监听的异步任务时,通过自己的属性来访问该值,不去访问全局作用域下的 i
2.2 立即执行函数
var btns = document.getElementsByTagName('button');
for(var i = 0;i<btns.length;i++){
(function (i) {
btns[i].onclick = function () {
alert(i);
}
})(i)
}
原理:将每次执行for循环的 i 当作实参传入函数,在外部function函数中的 i 是局部变量,所以每次添加监听的 i 是不同的。由于闭包和作用域链,i 会一直存在,在执行点击的异步任务时可以访问到每个作用域下的 i
2.3 用let替换var
var btns = document.getElementsByTagName('button');
for(let i = 0;i<btns.length;i++){
btns[i].onclick = function () {
alert(i);
}
}
原理:let发挥效果在块级作用域,每个for循环下的代码都在相互独立的块级作用域下,在执行点击监听函数时,内部function的 i 顺着作用域链不会找到全局作用域去,而是在块级作用域找到每个独立的 i
参考:循环遍历加监听的问题