在外層函數中,嵌套一個內層函數,那麼這個內層函數能夠向上訪問到外層函數中的變量。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>