什么是闭包?
函数与对其状态即词法环境(lexical environment)的引用共同构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在JavaScript,函数在每次创建时生成闭包。--出自MDN
词法环境:这个环境包含了这个闭包创建时所能访问的所有局部变量
当函数可以记住并访问所在词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。 - - 出自《你不知道的JavaScript(上卷)》
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为"闭包" - - 出自《JavaScript权威指南》
闭包的作用
- 延长内部变量的生命周期
- 可以读取函数内部的变量
例子
function fu() {
var a = 10
function fun() {
console.log(a); //闭包
}
fun()
}
fu()
function fu() {
var a = 10;
function fun() {
console.log(a)
}
return fun;
}
fu()();
问题: 循环绑定点击时间,输出为全局变量
<ul>
<li><button>点击1</button></li>
<li><button>点击2</button></li>
<li><button>点击3</button></li>
<li><button>点击4</button></li>
</ul>
<script>
const list = document.querySelectorAll("li button");
for (var index = 0; index < list.length; index++) {
list[index].addEventListener("click", function () {
console.log(index);
});
}
</script>
输出都是4
解决方案
- 使用闭包
<ul>
<li><button>点击1</button></li>
<li><button>点击2</button></li>
<li><button>点击3</button></li>
<li><button>点击4</button></li>
</ul>
<script>
const list = document.querySelectorAll("li button");
for (var index = 0; index < list.length; index++) {
(function (index) {
// 形成闭包作用域
//index局部变量永久储存
list[index].addEventListener("click", function () {
console.log(index);
});
})(index);
}
</script>
使用闭包永久保存变量
- 使用let关键字
function test() {
/**
局部变量 arr i
局部作用域
嵌套函数的闭包作用域
*/
var arr = [];
for (var i = 0; i < 10; i++) {
(function (j) {
/**
* 局部变量 j
* arr i 在此作用域引用,永久储存
* 嵌套函数的闭包作用域
*/
console.log(i); //上一级的局部变量在此作用域引用,都能永久储存
console.log(j);//当前作用域的变量下一个嵌套函数引用,将永久储存
arr[j] = function () {
/*
没有局部变量
j在此作用域引用,永久储存
*/
document.write(j + " "); // 输为0,1,2,3,4,5,6,7,8,9
};
})(i); //用立即执行函数(立即执行函数也可以生成自己的作用域) i作为参数传给j j不会随着i改变而改变
}
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
myArr[j]();
}
总结: 闭包就是在嵌套函数中引用上一级函数的局部变量,从而形成闭包作用域,引用的变量能够永久储存