在外层函数中,嵌套一个内层函数,那么这个内层函数能够向上访问到外层函数中的变量。javascript
既然内层函数能够访问到外层函数的变量,那若是把内层函数return出来会怎样?html
function outer () { var thing = '吃早餐'; function inner () { console.log(thing); } return inner; } var foo = outer(); foo(); // 吃早餐
函数执行完后,函数做用域的变量就会被垃圾回收。而这段代码看出当返回了一个访问了外部函数变量的内部函数,最后外部函数的变量得以保存。java
这种当变量存在的函数已经执行结束,但扔能够再次被访问到的方式就是“闭包”。跨域
1.闭包在红宝书中的解释就是:有权访问另外一个函数做用域中的变量的函数。数组
2.写法:缓存
1 <script type="text/javascript"> 2 function fun1(){ 3 var a = 100; 4 function fun2(){ 5 a++; 6 console.log(a); 7 } 8 return fun2; 9 } 10 11 var fun = fun1(); 12 fun() 13 fun() 14 </script>
3.效果以下:安全
4.分析:闭包
执行代码函数
GO{post
fun:underfined
fun1:function fun1()
{
var a = 100;
function fun2()
{
a++;
console.log(a);
}
return fun2;
}
}
而后第十一行开始这里,就是fun1函数执行,而后把fun1的return赋值给fun,这里比较复杂,咱们分开来看,
这里fun1函数执行,产生AO{
a:100
fun2:function fun2(){
a++;
console.log(a);
}
}
此刻fun1的做用域链为 第0位 AO
第1位 GO
此刻fun2的做用域链为 第0位 fun1的AO
第1位 GO
解释一下,fun2只是声明了,并无产生调用,因此没有产生本身的AO,
正常的,咱们到第7行代码咱们就结束了,可是这个时候来了一个return fun2,把fun2这个函数体抛给了全局变量fun,好了,fun1函数执行完毕,消除本身的AO,
此刻fun2的做用域链为 第0位 fun1的AO
第1位 GO
第十二行就是fun执行,而后,它自己是没有a的,可是它能够用fun1的AO,而后加,而后打印,
由于fun中的fun1的AO原本是应该在fun1销毁时,去掉,可是被抛给fun,因此如今fun1的AO没办法销毁,因此如今a变量至关于一个只能被fun访问的全局变量。
因此第十三行再调用一次fun函数,a被打印的值为102.
举例1:
1 <script type="text/javascript"> 2 function fun1(){ 3 var a = 100; 4 function fun2(){ 5 a ++; 6 console.log(a); 7 } 8 9 return fun2; 10 } 11 var fn1 = fun1(); //生成本身的AO,上面有a 12 var fn2 = fun1(); 13 fn1()//101 14 fn1()//102 15 fn2()//101 16 </script>
fn1和fn2互不干涉,由于fun1函数调用了两次,因此两次的AO是不同的。
举例2:
1 <script type="text/javascript"> 2 function fun(){ 3 var num = 0; 4 function jia(){ 5 num++; 6 console.log(num); 7 } 8 function jian(){ 9 num--; 10 console.log(num) 11 } 12 return [jia,jian]; 13 } 14 var fn = fun(); 15 var jia = fn[0]; 16 var jian = fn[1]; 17 jia()//1 18 jian()//0 19 </script>
jia和jian共用一个fun的AO,一动全都动,十二行返回了一个数组,
举例3:
1 <script type="text/javascript"> 2 function fun(){ 3 var num = 0; 4 function jia(){ 5 num++; 6 console.log(num); 7 } 8 function jian(){ 9 num--; 10 console.log(num) 11 } 12 return [jia,jian]; 13 } 14 var jia = fun()[0]; 15 var jian = fun()[1]; 16 jia()//1 17 jian()//-1 18 </script>
这里有一个坑,jia = fun()[0]; jian = fun()[1];fun函数执行了两遍,因此两次的AO不同,因此jia和jian操做的对象不同。
好处:
①保护函数内的变量安全 ,实现封装,防止变量流入其余环境发生命名冲突
②在内存中维持一个变量,能够作缓存(但使用多了同时也是一项缺点,消耗内存)
③匿名自执行函数能够减小内存消耗
坏处:
①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,形成内存泄漏,解决方法是能够在使用完变量后手动为它赋值为null;
②其次因为闭包涉及跨域访问,因此会致使性能损失,咱们能够经过把跨做用域变量存储在局部变量中,而后直接访问局部变量,来减轻对执行速度的影响
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> <script type="text/javascript"> var lis = document.getElementsByTagName("li"); for(var i = 0;i < lis.length;i++){ lis[i].onclick = function(){ console.log(i) } } </script> </body> </html>
无论点击哪一个都是10,那是由于点击事件是咱们点击才触发的函数,等到触发的时候,i早就变成10跳出循环了,,这个时候咱们就须要当即执行函数,创造了十个不一样的做用域
解决方案:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> <script type="text/javascript"> var lis = document.getElementsByTagName("li"); for(var i = 0;i < lis.length;i++){ // lis[i].onclick = function(){ // console.log(i) // } (function(i){ lis[i].onclick = function(){ console.log(i) } })(i) } </script> </body> </html>