Javascript之匿名函數(閉包與變量)


1.閉包與變量

 JavaScript中的作用域鏈的機制引出了一個副作用,即閉包只能取得包含函數中任何變量的最后一個值。閉包所保存的是整個變量對象,而不是某個特殊的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createFunctions(){ 
     var result= new Array(); 
        
     for ( var i=0;i<10;i++){ 
         result[i]= function (){ 
             return i; 
         }; 
    
     return result; 
var funcs = createFunctions(); 
for ( var i=0; i < funcs.length; i++){ 
     document.write(funcs[i]() + "<br />" ); 
}

createFunction()函數返回一個數組。表面上看,似乎每個函數都應該返回自己的索引值,但事實並非如此,事實上每個函數的返回值都是10.因為每個函數的作用域鏈中都包含着createFunctions()函數的活動對象,所以它們引用的都是同一個變量i。當createFunctions()函數返回后,變量i的值就是10,此時每個函數都引用着保存變量i的同一個變量對象,所以每個函數返回后都是10.

當然我們可以使用匿名函數強制使閉包的行為符合預期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createFunctions(){  
     var result= new Array();  
          
     for ( var i=0;i<10;i++){  
         result[i]= function (num){
             return function (){  
             return num;  
         };  
         }(i);
     }  
     return result;  
}  
var funcs = createFunctions();  
for ( var i=0; i < funcs.length; i++){  
     document.write(funcs[i]() + "<br />" );  
}

在重寫了前面的createFunctions()函數后,每個函數就好返回各自不同的索引值了。在這里,我們沒有直接把閉包賦值給數值,而是定義了一個匿名函數,並將立即執行該函數的結果賦值給數組。這里的匿名函數有一個參數num,也就是最終的函數要返回的值。在調用每個匿名函數時,我們傳入了變量i。由於函數參數按值傳遞的,所以就會將變量i的當前值復制給參數num。而在這個匿名函數內部,有創建並返回了一個訪問num的閉包。這樣依賴,result數組中的每個函數都有自己num變量的一個副本,因此就可以返回各自不同的數值了。

1.2關於this對象

在閉包中使用this對象會出現一些問題,this對象是運行時基於函數的執行環境綁定的:在全局函數中,this等於window,而當函數被當作某個對象的方法調用時,this等於那個對象。不過,匿名函數的執行環境具有全局性,因此其this對象通常指向window(當然,在通過call()和apply()改變函數執行環境時,this指向其他對象)。

1
2
3
4
5
6
7
8
9
10
11
var name= "The Window"
    
var object={ 
     name: "My object"
     getNameFunc: function (){ 
         return function (){ 
             return this .name; 
             }; 
        
     }; 
alert(object.getNameFunc()()); //"The Window"(在非嚴格模式下)

以上代碼創建了一個全局變量name,有創建了一個包含那么屬性的對象,這個對象還包括一個方法——getNameFunc(),它返回一個匿名函數,而匿名函數又返回this.name.由於getNameFunc()返會一個函數。因此調用object.getNameFunc()()就會立即返回調用它的函數,結果就返回一個字符串。然而,這個例子返回的字符串是“The Window”,即全局name變量的值。

但是,為什么匿名函數沒有取得其包含作用域(或外部作用域)的this對象呢?

每個函數在調用時,其活動對象都會自動獲取兩個特殊的變量:this和arguments。內部函數在搜索這兩個變量時,只會搜到其活動對象為止,因此永遠不肯能訪問到外部函數中的這兩個變量。不過,把外部作用域中的this對象保存在一個閉包能夠訪問的變量里,就可以放閉包訪問該對象了。

1
2
3
4
5
6
7
8
9
10
11
12
var name= "The Window"
    
var object={ 
     name: "My object"
     getNameFunc: function (){ 
         var that= this ;
         return function (){ 
             return that.name; 
             }; 
        
     }; 
alert(object.getNameFunc()()); //"My object"

 以上代碼中,我們在定義匿名函數之前,把this對象賦值給了that變量,而在定義閉包之后,閉包也可以訪問這個變量,因為它們是我們在外部函數中特意聲明的一個變量。即使在函數返回之后,this也仍然引用的object,所以調用object.getName()()就返回“My object”.

1.3內存泄漏

由於IE9之前的版本對JS對象和COM對象使用不同的垃圾回收歷程,因此閉包在IE中會導致一些特殊的問題。具體來說,如果閉包的作用域鏈中保存着一個HTML元素,那么就意味着該元素將無法被銷毀。

1
2
3
4
5
6
function assignHandler(){
     var element=document.getElementById( "someElement" );
     element.onclick= function (){
          alert(element.id);
     };
}

以上代碼創建了一個作為element元素事件處理程序的閉包,而這個閉包則有創建了一個循環引用。由於匿名函數保存了對assignHandler()的活動對象的引用,因此就會導致無法減少element的引用數,也就無法回收該對象。

不過可以通過一下代碼來解決。

1
2
3
4
5
6
7
8
9
function assignHandler(){ 
     var element=document.getElementById( "someElement" ); 
     var id=element.id;
  
     element.onclick= function (){ 
          alert(id); 
     }; 
     element= null ;
}

 






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM