闭包:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。一个作用域可以访问另外一个函数的局部变量。
闭包的作用:1.可以读取函数内部的变量,延长变量的作用域 2.让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在
1 function A() {
2 let a = 1
3 window.B = function() {
4 console.log(a)
5 }
6 }
7 A()
8 B() // 1
这里B就是闭包,单纯地说闭包就是函数嵌套了函数,然后返回一个函数,是不完整的,上面的例子很好地反驳了。
1 function f1() {
2 let a = 1
3
4 function f2() {
5 console.log(a);
6 }
7 return f2;
8 }
9 var res = f1();
10 res(); // 1
这里闭包就是函数f2,能够读取f1函数内部的变量(闭包就是一个能读取其他函数内部变量的一个函数,简单来说就是一个函数)
1 function create(start) {
2 return function() {
3 console.log(start++);
4 }
5 }
6 var inc = create(5);
7 inc(); // 5
8 inc(); // 6
9 inc(); // 7
这里就是闭包使得内部变量记住了上一次调用的结果。
经典面试题,循环中使用闭包解决 `var` 定义函数的问题
1 for (var i = 0; i < 6; i++) {
2 setTimeout(function timer() {
3 console.log(i);
4 }, i * 1000)
5 }
首先因为 setTimeout
是个异步函数,所以会先把循环全部执行完毕,这时候 i
就是 6 了,所以会输出一堆 6。(同理,click等点击事件是回调函数,for循环是同步任务,就会先把循环执行完毕)
解决办法有三种,第一种是使用闭包的方式
1 for (var i = 0; i < 6; i++) {
2 (function(j) {
3 setTimeout(function timer() {
4 console.log(j);
5 }, j * 1000)
6 })(i)
7 }
在上述代码中,我们首先使用了立即执行函数将 i
传入函数内部,这个时候值就被固定在了参数 j
上面不会改变,当下次执行 timer
这个闭包的时候,就可以使用外部函数的变量 j
,从而达到目的。
第二种就是使用 setTimeout
的第三个参数,这个参数会被当成 timer
函数的参数传入。
1 for (var i = 0; i < 6; i++) {
2 setTimeout(function timer(j) {
3 console.log(j);
4 }, i * 1000, i)
5
6 }
第三种就是使用 let
定义 i
了来解决问题了,这个也是最为推荐的方式
1 for (let i = 0; i < 6; i++) {
2 setTimeout(function timer() {
3 console.log(i);
4 }, i * 1000)
5 }