深入理解闭包 Minoz

闭包之前一直都在看,却总感觉没有深入理解,一直处于云里雾里,今天终于可以炫耀的说我懂了!

闭包是什么?

官方解释我就不说了,只说我理解的吧~

闭包是提供给外部访问函数内部私有变量的一个接口

一个函数里定义另一个函数就会产生闭包

解释一下:

function func() {
    var a = 10;
    return function() {
        return a;
    };
}
var b = func();
console.log(b());

在函数里定义变量都是私有的,外面无法访问到函数内部,那么,就可以通过闭包,将私有变量返回,外界就可以访问到func函数里的变量了。所以,我说闭包是提供给外部访问函数内部私有变量的一个接口。

了解闭包的作用域链

此处已经在我的博客的上一篇深入理解作用域说过了,大家可以参考,这里就不再累赘。

闭包注意的几个小问题

 我们来看几道题逐渐理解闭包

1.循环中的闭包

1>请注意第6行

 1 function func(list) {
 2     var arr = [];
 3     for(var i = 0; i < list.length; i++) {
 4         var index = i;
 5         arr.push(function(){
 6             console.log('index为' + index); //index
 7             console.log('list为' + list[i]);
 8         });
 9     }
10     return arr;
11 }
12 function output() {
13     var list = [1, 2, 3];
14     var all = func(list);
15     for(var i = 0; i < all.length; i++){
16         all[i]();
17     }
18 }
19 output();

结果为3个    index为2   list为undefined

2>请注意第6行

 1 function func(list) {
 2     var arr = [];
 3     for(var i = 0; i < list.length; i++) {
 4         var index = i;
 5         arr.push(function(){
 6             console.log('i为' + i);  //i
 7             console.log('list为' + list[i]);
 8         });
 9     }
10     return arr;
11 }
12 function output() {
13     var list = [1, 2, 3];
14     var all = func(list);
15     for(var i = 0; i < all.length; i++){
16         all[i]();
17     }
18 }
19 output();

结果为3个    i为3   list为undefined

结果是不是很出乎意料,之前我因为这个问题纠结了好长时间,只怪当初太年轻~~

在for循环中的使用闭包调用的是同一个闭包,因此无论for循环几次,都是最后一个值。那么,为什么是undefined呢?

这里不得不提到javascript中没有块级作用域,因此虽然看似是在for循环中声明了变量 i,但实际上i的作用域是func函数。并且闭包中局部变量是引用并不是

拷贝,因此,当退出循环时i为3,闭包中的i也就为3,list[3](list[2]为最后一个值)不存在,所以为undefined。

将上述第7行代码改为下面的代码

1 console.log('list为' + list[index]); //list[index]

将输出  list为3

index值自然是for循环中最后的一个i值为2。

解决办法

知道是什么原因了,那如何解决呢?

 1 function func(list) {
 2     var arr = [];
 3     for(var i = 0; i < list.length; i++) {
 4         var index = i;
 5         arr.push(
 6             (function(j){
 7             console.log('list为' + list[j]);
 8             })(i)
 9         );
10     }
11 }
12 func([1, 2, 3]);

输出  list为1  list为2  list为3

添加另一个闭包,并传参使其立即执行,参数是按值传递的,所以每一次传过去的值都是不同的值,得出我们想要的答案。

2.闭包中的this对象

 1 var num = 10;
 2 var obj = {
 3     num: 'nana',
 4     getNum: function() {
 5         return function(){
 6             return console.log(this.num);
 7         }
 8     }
 9 }
10 obj.getNum()();

输出结果为:10

是不是又很神奇,在闭包中this是指向window的,所以,当我们访问this.a时,访问的是全局变量a。那么,想要访问函数func中的a呢?

解决办法

 1 var num = 10;
 2 var obj = {
 3     num: 'nana',
 4     getNum: function() {
 5         var that = this;
 6         return function(){
 7             return console.log(that.num);
 8         }
 9     }
10 }
11 obj.getNum()();

输出结果为:nana

3.变量回收

1 function func() {
2     var a = 0;
3     return function(){ 
4         return ++a;
5     };
6 }
7 var f = func();
8 console.log(f());
9 console.log(f());

输出结果:1    2

在函数中的局部变量会随着函数执行完毕而被回收,可是,func函数引用着闭包函数,闭包中引用着局部变量a,因此,变量不会被js回收机制回收。第一次

调用f时a加1,第二次调用时,由于a未被回收,所以又在1的基础上又加了1为2。

那么,js中回收机制是怎么工作的呢?

在js中如果对象不再引用,则会被回收,如果两个对象互相引用不再被第三者引用也会被回收,但如果还被第三者引用就不会回收,上述由于处于这种情况所

以变量a不会被回收。

由于无法回收局部变量,会导致性能问题,因此,要避免滥用闭包,在闭包使用完毕时,需删除局部变量。

4.多个函数绑定同一闭包

 1 function func() {
 2     var num = 10;
 3     getNum = function() {
 4         console.log(num);
 5     };
 6     countNum = function() {
 7         num++;
 8     };
 9     setNum = function(x) {
10         num = x;
11     };
12 }
13 func();
14 getNum();
15 countNum();
16 getNum();
17 setNum(20);
18 getNum();

输出结果为10   11    20

5.闭包外部函数所有局部变量都在闭包内,声明在闭包函数后的局部变量也可访问

1 function func() {
2     function a(){
3         console.log(num);
4     };
5     var num = 10;
6     return a;
7 }
8 func()();

结果输出  10

6.函数调用时访问的是不同的闭包

 1 function func(list) {
 2     var arr = [];
 3     for(var i = 0; i < list.length; i++) {
 4         var index = i;
 5         arr.push(
 6             (function(j){
 7             console.log(list[j]);
 8             })(i)
 9         );
10     }
11 }
12 
13 func([1, 2, 3]);
14 func([6, 7, 8]);

输出结果1,2,3   6,7,8

上面举了这么多例子,每个例子都不同,如果想完全理解的话只有当你遇到上述问题时,才会让你印象深刻,所以还是乖乖的去敲代码吧!

最后来个小测试

1 for(var i = 0; i < 5; i++) {
2     var index = i;
3     (function(j){
4         setTimeout(function(){
5             console.log(j);
6         },10);
7     })(i)
8 }

输出结果 0 1 2 3 4

1 for(var i = 0; i < 5; i++) {
2     var index = i;
3     (function(j){
4        setTimeout(function(){
5             console.log(index);
6         },10);
7     })(i)
8 }

输出结果 4 4 4 4 4

1 for(var i = 0; i < 5; i++) {
2     var index = i;
3     (function(j){
4         setTimeout(function(){
5             console.log(i);
6         },10);
7     })(i)
8 }

输出结果 5 5 5 5 5

猜对了么?如果没有,就再看看上面例子吧~

原文地址:https://www.cnblogs.com/nalixueblog/p/4474738.html