什么是闭包:
在函数执行时建立属于自己的私有作用域,保护里面的私有变量不受外界干扰。
例题一
下面是无闭包的情况下,运行结果为:每隔1秒打印1个5
<script>
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
</script>
原因分析:
把上面代码分解开,运行先后顺序如下所示,for循环是一瞬间完成的,变量 i 的最终结果在最下面为5(5不满足<5,跳出循环),这时候setTimeout延迟调用才刚刚开始,可以理解5个setTimeout几乎同时开始,就相当于每隔一秒调用一次a函数,然后打印5个5出来。
<script>
var i,a;
function a () {
console.log(i);
}
i = 0;
setTimeout(a, i * 1000);
i = 1;
setTimeout(a, i * 1000);
i = 2;
setTimeout(a, i * 1000);
i = 3;
setTimeout(a, i * 1000);
i = 4;
setTimeout(a, i * 1000);
i = 5;
</script>
解决方法一:
使用匿名自执行函数,语法为:(匿名函数)(),
代码如下,其中实参为i,形参为x,循环一次,i++一次,同时 i 进入匿名函数传给x一次,每次打印的x不受外界的影响,相当于建立了自己的私人空间。
<script>
for (var i = 0; i < 5; i++) {
(function (x) {
setTimeout(function () {
console.log(x);
}, x * 1000);
})(i);
}
</script>
解决方法二:
将var声明改为let声明,这是因为let一个特点:有块级作用域。
<script>
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
</script>
例题二
下面是无闭包的情况下,运行结果为:无论点击哪一个标签 li ,都打印出5,
<ul>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
<li>列表4</li>
<li>列表5</li>
</ul>
<script>
var lis = document.querySelectorAll("ul li");
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
console.log(i);
}
}
</script>
原因分析和解决方法与例题一类似,同样使用匿名自执行函数,废话少说,代码如下,
<ul>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
<li>列表4</li>
<li>列表5</li>
</ul>
<script>
var lis = document.querySelectorAll("ul li");
for (var i = 0; i < lis.length; i++) {
(function (ii) {
lis[ii].onclick = function () {
console.log(ii);
}
})(i)
}
</script>