淺談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 })();
通過上面的程式碼,我們能夠當前的日期和時間。