# 闭包是什么?
在JavaScript中,只有函数内部的子函数才能读取到局部变量,闭包可以理解为,定义在一个函数内部的函数, 在本质上,闭包是将函数内部和外部连接起来的桥梁
# 实现一个闭包
function name1() {
let a = 12;
return function name2() {
console.log(a)
};
}
let b= name1();
b();//12
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
闭包的定义:有权访问另一个函数作用域的函数
# 闭包的应用
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
1
2
3
4
5
2
3
4
5
let lis = document.querySelectorAll("li");
console.log(lis);
for (var i = 0; i < 3; i++) {
lis[i].onclick = function () {
console.log(i);
};
}
1
2
3
4
5
6
7
2
3
4
5
6
7
假如使用var,点击每一个li,输出的都是3。
var没有块级作用域,循环0,1,2之后,最后一次又加1,最后i的值就是3,之后的值会覆盖前面的值,
我们先给每一个li绑定事件,当你点击li的时候,才会触发onclick事件,但是此时for循环早就循环完了,最终的i就是3
所以无论你点哪个li,结果都是3
PS:js中的事件和定时器都是异步的,上面的循环执行完了之后,才会执行异步代码,i会被覆盖。
# 如何解决
# 方案1:存储索引值
let lis = document.querySelectorAll("li");
for (let i = 0; i < 3; i++) {
//在这里把i存储到index里
lis[i].index = i;
lis[i].onclick = function () {
console.log(this.index);
};
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 方案2:使用let
把var改成let就可以了
let lis = document.querySelectorAll("li");
for (let i = 0; i < 3; i++) {
lis[i].onclick = function () {
console.log(i);
};
}
1
2
3
4
5
6
2
3
4
5
6
因为let有块级作用域,每一个li的i都是不一样的,有自己的作用域,不会被覆盖
# 方案3:使用闭包
function help(i) {
return () => {
console.log(i);
};
}
for (var i = 0; i < 3; i++) {
lis[i].onclick=help(i)
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
这里必须要添加闭包,也就是此处的匿名函数
lis[i].onclick=help(i),将i传递给help函数,并且调用,help执行之后
就返回一个函数,而这个函数正好可以访问help中的变量
所以点击的时候,就会调用这个匿名函数,然后就会输出对应的0,1,2
# 方案4:使用闭包+立即执行函数
for (var i = 0; i < 3; i++) {
(function (index) {
lis[index].onclick = function () {
console.log(index);
};
})(i);
}
1
2
3
4
5
6
7
2
3
4
5
6
7
index是形参,i是实参。内部的function是闭包
# 闭包的坏处
正常函数执行完了之后,会在内存中销毁
但是闭包因为使用了其他函数的中的变量,其他函数就不会销毁,会一直存在
那么就会造成内存浪费。