for循环为多个对象添加时间侦听器-闭包问题

闭包问题

今天要说的是JavaScript的闭包问题,先来看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script type="text/javascript">
for (var i = 0; i < json["length"]; i++) {
x += "<td>" + json[i]["contents"] + "</td>";
var row = document.createElement('tr');
row.innerHTML = x;
var op = document.createElement('button');
op.innerText = "购买";
op.setAttribute('style', 'height: 35px; width: 70px; padding-right: 10px; margin: 0 auto;');
op.addEventListener('click', function () { buyLoan(json[i]); }, false);
row.appendChild(op);
tbody.appendChild(row);
x = "";
}

</script>

上边的代码中”json”是获取的数据信息,遍历数据信息,添加购买按钮。

这是一个很典型的闭包问题,因为JavaScript没有块级作用域(for, if, while),var定义的变量会变为全局变量。
这种情况发生之下就是,当代码运行时,每运行一次i都会被覆盖,始终是最后的一个元素。

解决方法

使用let定义变量

从ES6开始,就可以使用let来定义块级作用域的变量了。

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
'use strict'

let demo = 0;
console.log(demo);

</script>


使用立即执行函数解决

立即执行函数最常用的场景就是将var变量的作用域限制在你的函数里,这样可以避免命名冲突和变量变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script type="text/javascript">

for (var i = 0; i < json["length"]; i++) {
(function (i) {
x += "<td>" + json[i]["contents"] + "</td>";
var row = document.createElement('tr');
row.innerHTML = x;
var op = document.createElement('button');
op.innerText = "购买";
op.setAttribute('style', 'height: 35px; width: 70px; padding-right: 10px; margin: 0 auto;');
op.addEventListener('click', function () { buyLoan(json[i]); }, false);
row.appendChild(op);
tbody.appendChild(row);
x = "";
}(i));
}

</script>

解决来源

StackOverFlow
var与let的区别