闭包之前一直都在看,却总感觉没有深入理解,一直处于云里雾里,今天终于可以炫耀的说我懂了!
闭包是什么?
官方解释我就不说了,只说我理解的吧~
闭包是提供给外部访问函数内部私有变量的一个接口
一个函数里定义另一个函数就会产生闭包
解释一下:
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
猜对了么?如果没有,就再看看上面例子吧~