javascript:閉包 JavaScript:做用域與做用域鏈 javascript預編譯的過程

在閱讀本篇文章以前,能夠先參考個人JavaScript:做用域與做用域鏈javascript預編譯的過程,能夠更好的理解

閉包在紅寶書中的解釋就是:有權訪問另外一個函數做用域中的變量的函數。

咱們先總結一下什麼是閉包:

  • 何時須要用到閉包:
    • 須要在一個函數外部,訪問函數內部的變量的時候(也就是說在函數運行完以後,你想要把變量保存下來,待須要的時候調用。而不是經過垃圾回收機制消除(garbage collection))。
    • 保護變量安全。一個函數的內部變量,只能內部函數引用。
  • 如何定義閉包:
    • 在一個函數內部,定義一個函數,並返回一個函數的引用。

 

 

在外層函數中,嵌套一個內層函數,那麼這個內層函數能夠向上訪問到外層函數中的變量。javascript

既然內層函數能夠訪問到外層函數的變量,那若是把內層函數return出來會怎樣?html

function outer () {
    var thing = '吃早餐';
    
    function inner () {
        console.log(thing);
    }
    
    return inner;
}

var foo = outer();
foo();  // 吃早餐

函數執行完後,函數做用域的變量就會被垃圾回收。而這段代碼看出當返回了一個訪問了外部函數變量的內部函數,最後外部函數的變量得以保存。java

這種當變量存在的函數已經執行結束,但扔能夠再次被訪問到的方式就是「閉包」。跨域

1.閉包

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.

2.閉包之深刻理解

舉例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操做的對象不同。

3.閉包好處與壞處

好處:

①保護函數內的變量安全 ,實現封裝,防止變量流入其餘環境發生命名衝突

②在內存中維持一個變量,能夠作緩存(但使用多了同時也是一項缺點,消耗內存)

③匿名自執行函數能夠減小內存消耗

壞處:

①其中一點上面已經有體現了,就是被引用的私有變量不能被銷燬,增大了內存消耗,形成內存泄漏,解決方法是能夠在使用完變量後手動爲它賦值爲null;

②其次因爲閉包涉及跨域訪問,因此會致使性能損失,咱們能夠經過把跨做用域變量存儲在局部變量中,而後直接訪問局部變量,來減輕對執行速度的影響

4.閉包解決的問題

<!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>
相關文章
相關標籤/搜索