1. 程式人生 > 其它 >淺談JavaScript的函式表示式(閉包)

淺談JavaScript的函式表示式(閉包)

  前文已經簡單的介紹了函式的閉包。函式的閉包就是有權訪問另一個函式作用域的函式,也就是函式內部又定義了一個函式。

1 var Super=function(num){
2                         var count=num;
3                         return function(){
4                             console.log(count);
5                         }
6                     }
7                     var result=Super(3);//此時result是一個函式
8                     result();//輸出3

  上面的程式碼定義了一個函式Super,同時在Super函式內部又定義了一個匿名函式作為返回值。第七行呼叫Super函式,此時result是一個函式。第8行執行了result函式,輸出為3。這就是閉包的體現,因為上面的Super函式已經執行結束,但是它的內部變數count的值依然沒有被釋放,count的還在被匿名函式引用,所以沒有辦法釋放。如果要釋放count,我們需要 result=null ,將null賦值給result。

  在前文已經介紹了函式的作用域鏈,當函式第一次被呼叫時會建立一個作用域鏈,並作用域鏈賦值給一個特殊的內部屬性。在作用域鏈中,函式的外部函式的活動物件位於第二位,外部函式的外部函式的活動物件位於第三位,以此類推,全域性變數的作用域鏈位於最底部。

 1 var count=2;
 2 console.log(count);//2
 3 count=3;
 4 console.log(count);//3
 5 var Super=function(num){
 6 var count=num;//作用域鏈第二位
 7 console.log(count);//1
 8 return function(){
 9 var count=5;//作用域鏈第一位
10 console.log(count);
11 }
12 }
13 var result= new Super(1);
14 result();//輸出5

  上面的程式碼中,能夠清晰地瞭解到變數的作用域。閉包函式的內部變數位於最頂端,全域性變數位於最底部。

  在閉包中使用this物件也可能會導致一些問題,this物件是在函式執行時,基於函式的執行環境繫結的。在全域性函式中,this指向window物件。而函式作為某個物件的方式呼叫時,this等於那個物件。不過匿名函式的執行環境具有全域性性,因此this指向window

 1 var obj={
 2                         name:"heh",
 3                         getName:function(){
 4                             var that=this;
 5                             return function(){
 6                                 return that.name;
 7                             }
 8                         }
 9                     };
10                     var one=obj.getName();
11                     var name=one();//heh

  上面的程式碼中,通過字面量的方式建立了物件obj,定義了物件的屬性name和方法getName。但是在getName內部,我們定義了閉包函式。如果想在閉包函式中訪問name,通過this是訪問不到的。所以需要在閉包函式的外部定義一個變數that,指向this。在getName中定義的變數,在閉包函式中仍然可以繼續使用。

  JavaScript中沒有塊級作用域的概念,這意味著塊級中定義的變數,實際在函式內部都是可以使用的。

1 for(var i=0;i<10;i++){
2                         console.log(i);
3                     }
4                     console.log(i);//輸出10

  上面的程式碼中,我們在for迴圈中定義了變數i,但是我們在for迴圈外部依然可以使用i。for迴圈結束後,i變數並沒有被銷燬。

  JavaScript可以使用匿名函式來模仿塊級作用域,從而避免該類問題的發生。

1 (function(){
2                         for(var i=0;i<10;i++){
3                             console.log(i);
4                         }
5                     })();
6                     console.log(i);

  上面的程式碼中,我們將塊級作用域放在了一個匿名函式中,同時將匿名函式放在一對括號中,這表示一個函式表示式。在函式表示式外部的括號,表示立即呼叫該函式。在第六行呼叫該函式的時候,會發生報錯,因為i並沒有定義。

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

  這個程式碼,和我們上面的程式碼是一樣的,都是通過函式表示式來定義函式。

1 function(){
2                         for(var i=0;i<10;i++){
3                             console.log(i);
4                         }
5                     }();

  上面的程式碼是錯誤的。我們知道函式的定義方法,可以通過function和函式表示式。通過function宣告函式的時候,function後面不能跟圓括號。函式表示式的後面可以跟圓括號。

1 var testFunc=function(){
2                         for(var i=0;i<10;i++){
3                             console.log(i);
4                         }
5                     }();

  一般來說在開發過程中,應該儘量少向全域性物件中新增函式和變數。太多的全域性函式和變數容易導致命名的衝突以及記憶體的洩露。我們可以在塊級函式中完成所有的操作。

1 (function(){
2                         var now = new Date();
3                         console.log(now.getFullYear()+"-"+(now.getMonth()+1)+"-"+now.getDate()+" "+now.getHours()+":"+now.getMinutes()+":"+now.getSeconds());
4                     })();

  通過上面的程式碼,我們能夠當前的日期和時間。