Professional javascript For Web Developers 第2版讀書筆記之閉包

來源:互聯網
上載者:User

首先什麼是匿名函數?

匿名函數就是沒有名字的函數。

為什麼要有沒有名字的函數,而不給每個函數都起名字?

有些功能在某個範圍內只用一次而且很簡單,沒必要取個名字(當然取名字也可以),但是增加了代碼冗餘,因為這些取名字的工作都是在聲明函數,聲明函數是個苦力活,因為你一直在敲那些重複的function後面跟函數名,同時還要注意命名還不能跟已有函數重名,否則會覆蓋。最重要的是減少了代碼量卻實現了相同的功能,維護的時候更方便。

function functionName(arg0, arg1, arg2) {
    //function body

var functionName = function(arg0, arg1, arg2) {
    //function body
}; 

前者是聲明函數,在運行之前就會載入到相應的上下文(不論變數還是函數,瀏覽器都是先運行聲明產生類似先行編譯的效果),後者是把函數作為一個運算式賦值給一個變數,只有當啟動並執行時候才載入。

 

什麼是閉包?

閉包就是一個函數,這個函數能訪問其他函數的範圍內的變數。根據範圍鏈的規則要實現這個功能基本上只能靠嵌套函數實現,並且這個閉包函數是作為父函數的傳回值返回,而且這個閉包函數通常是個匿名函數。

 

function createComparisonFunction(propertyName) {
                   
    return function(object1, object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
                   
        if (value1  <  value2){
            return -1;
        } else if (value1  >  value2){
            return 1;
        } else {
            return 0;
        }
    };

此例中內部的匿名函數訪問了父函數的propertyName變數,而且當匿名函數作為createComparisonFunction的傳回值return了之後,這個匿名函數仍然能訪問這個propertyName。為什麼呢?

 

當一個函數被調用時,一個處於執行的上下文環境被建立,這個上下文環境建立了一個範圍鏈並且指派給了內部屬性(Scope),同時一個使用中的物件被初始化,這個使用中的物件擁有this,arguments和其他顯示命名的參數。然後Scope鏈中指向的第一個對象便是這個使用中的物件,緊接著,父函數也建立出了一個使用中的物件,類似的過程,Scope中指向的第2個對象是這個父函數的使用中的物件,這個過程一直持續向上遞迴,直到最後的父函數是當前的window對象或者說Scope鏈的最頂層指向的當前的全域上下文即window對象

當匿名函數作為傳回值從createComparisonFunction返回時,該匿名函數的範圍鏈才初始化出了3層對象(),同時createComparisonFunction的使用中的物件不能銷毀因為當createComparisonFunction傳回值時,內部的匿名函數的範圍鏈仍然有createComparisonFunction的使用中的物件,銷毀的只是createComparisonFunction函數的範圍鏈,createComparisonFunction函數的使用中的物件仍然駐留記憶體中直到匿名函數銷毀

 

 

//create function
var compareNames = createComparisonFunction(“name”);
                   
//call function
var result = compareNames({ name: “Nicholas” }, { name: “Greg”});
                   
//dereference function - memory can now be reclaimed
compareNames = null; 

設定compareNames=null使匿名函數沒有被賦值給任何變數,因此可以被記憶體回收。

 

閉包有一個值得注意的特點是:閉包訪問的父函數的變數,這個變數總是所有操作後最後一次的值

 

function createFunctions(){
    var result = new Array();
    
    for (var i=0; i  <  10; i++){
        result[i] = function(){
            return i;
        };
    }
    
    return result;

此例中,10個匿名函數在各自的範圍鏈中都擁有createFunctions的使用中的物件,並且它們都指向了相同的變數i,當createFunctions結束執行時,i的值已經是10了。

如果要避免這個問題,可以做如下修改

function createFunctions(){
    var result = new Array();
    
    for (var i=0; i  <  10; i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i);
    }
    
    return result;

因為函數是值傳遞,因此當i作為匿名函數參數傳遞給num時,只是匿名函數內部會產生一個i的副本,此時匿名函數內部沒有其他指向i的引用。

 

匿名函數中的this

var name = “The Window”;
                   
var object = {
    name : “My Object”,
                   
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
                   
alert(object.getNameFunc()());  //”The Window” 

 匿名函數被認為是定義在全域上下文window中的,因此this總是指向window,從前面的圖中也可以知道。

如果要讓匿名函數訪問父函數的name,則應修改為:

var name = “The Window”;
                   
var object = {
    name : “My Object”,
                   
    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
                   
alert(object.getNameFunc()());  //”My Object” 

匿名函數總是擁有父函數的“使用中的物件

閉包的一個缺點是記憶體流失問題:

 

function assignHandler(){
    var element = document.getElementById(“someElement”);
    element.onclick = function(){
        alert(element.id);
    };

此例中匿名函數作為element元素的onclick事件的處理函數,因此父函數和匿名函數產生了一個循環參考(匿名函數擁有父函數的使用中的物件),只要匿名函數存在,對element對象的引用始終存在,記憶體也不會被回收。

如要避免此問題,做如下修改

function assignHandler(){
    var element = document.getElementById(“someElement”);
    var id = element.id;
                   
    element.onclick = function(){
        alert(id);
    };
                   
    element = null;

此例中id作為element的id的副本被匿名函數引用,匿名函數中並沒有直接引用父函數的變數,但是匿名函數仍然擁有父函數的使用中的物件,為了消除這種引用,設定element為null,因而消除了對idsomeElement的dom對象的引用

 

 

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.