JavaScript中的閉包

來源:互聯網
上載者:User

標籤:cat   前端   catch   關係   過多   val   array   bar   span   

  有不少開發人員總是搞不清匿名函數和閉包這兩個概念,因此經常混用。閉包是指有權訪問另一個函數範圍中的變數的函數。建立閉包的常見方式,就是在一個函數內部建立另一個函數。看下面的例子

 1 function createComparisonFunction(propertyName) { 2   return function(object1, object2) { 3     var value1 = object1[propertyName]; 4     var value2 = object2[propertyName]; 5     if (value1 < value2) { 6       return -1; 7     } else if (value1 > value2) { 8       return 1; 9     } else {10       return 0;11     }12   }13 }14 15 var clo = createComparisonFunction(‘name‘);16 var res = clo({‘name‘:‘Jeff‘}, {‘name‘:‘Tim‘});17 console.log(res);

  至於inner function為什麼能夠訪問parent function中的變數(這裡指的是propertyName)?

  我們需要先來搞清楚幾個概念,執行環境(execution context),使用中的物件(activation object),變數對象(variable object),範圍鏈(scope chain)

  什麼是執行環境?可以抽象的理解成是一個Object,它有一系列的屬性,也叫做當前執行環境的狀態。看下面的圖表,除了這三個必要的屬性,一個執行環境也可能會有其它的屬性,這取決於它的實現。  

  

  什麼是變數對象?變數對象是和當前執行環境相關聯的一個資料容器,它是一個特殊的對象,儲存著當前執行環境中的變數和函式宣告,注意,不包括函數運算式,在不同的執行環境中,它指代的對象也不同,例如,在全域環境中,變數對象就是全域對象本身,這也是為什麼我們能夠通過全域對象(window)的屬性引用全域變數了,那在function context中,變數對象又是什麼呢?在函數範圍中,變數對象可認為是使用中的物件(activation object)

  那什麼是使用中的物件呢?當一個函數被調用的時候使用中的物件會被建立,調用這個函數的主體我們稱之為caller,caller也是一個特殊的對象,使用中的物件包含函數的參數,特殊的arguments對象(一個函數參數和索引的map),還有函數內部的變數和函式宣告。在函數範圍中,使用中的物件被作為是當前環境中的變數對象使用。看下面的例子

1 function foo(x, y) {2   var z = 30;3   function bar() {} // FD4   (function baz() {}); // FE5 }6  7 foo(10, 20);
  foo function context的使用中的物件是這樣的  

  什麼是範圍鏈呢?A scope chain is a list of objects that are searched for identifiers appear in the code of the context.規則類似於prototype chain,如果某個變數標識符在當前範圍中沒有找到,則到parent的範圍(變數對象)中去尋找,一層層往上尋找,這樣在當前環境中沒有尋找到需要到上層才能查到的變數稱之為free variable。而free variable的尋找則是通過範圍鏈實現的,通常來說,一個範圍鏈就是一系列的parent variable objects 加上 函數自身的變數對象或使用中的物件,然而,這個範圍鏈也可能包含一些在環境執行期間被動態添加的對象,例如使用with 聲明 或 catch。

  來看下面的例子

 1 function compare(value1, value2) { 2   if (value1 < value2) { 3     return -1; 4   } else if (value1 > value2) { 5     return 1; 6   } { 7     return 0; 8   } 9 }10 11 var result = compare(5, 10);

 

  展示了包含上述關係的compare函數執行時的範圍鏈

        

  全域環境的變數對象始終存在,而像compare()函數這樣的局部環境的變數對象,則只在函數執行的過程中存在。注意這些對象的先後順序,在建立compare函數時,會建立一個預先包含全域變數對象的範圍鏈,這個範圍鏈被儲存在內部的[[Scope]]屬性中。當調用compare函數時,會為函數建立一個執行環境,然後通過複製函數的[[Scope]]屬性中的對象構建起執行環境的範圍鏈。此後,又有一個使用中的物件(在此作為變數對象使用)被建立並被推入執行環境範圍鏈的前端。對於這個例子中compare函數的執行環境而言,其範圍鏈中包含兩個變數對象,本地使用中的物件和全域變數對象。顯然,範圍鏈本質上是一個指向變數對象的指標列表,它只引用但不實際包含變數對象。

  下面回到最初的那個閉包函數,看一下它的範圍鏈

  在一個函數內部定義的函數會將包含函數(即外部函數)的使用中的物件添加到它的範圍鏈中。因此在createComparisonFunction內定義的匿名函數的範圍鏈中,實際上會包含外部函數createComparisonFunction的使用中的物件。在匿名函數被返回後,它的範圍鏈被初始化為包含createComparisonFunction的使用中的物件和全域變數對象,這樣匿名函數就可以訪問在createComparisonFunction中定義的變數了。更為重要的是,createComparisonFunction函數在執行完畢後,其使用中的物件也不會被銷毀,因為匿名函數的範圍鏈仍然在引用這個使用中的物件,換句話說,createComparisonFunction函數返回後,其執行環境的範圍鏈會被銷毀,但它的使用中的物件仍然會留在記憶體中;直到匿名函數被銷毀,createComparisonFunction的使用中的物件才會被銷毀,這裡執行代碼clo = null即可銷毀匿名函數。

  由於閉包會攜帶包含它的函數的範圍,因此會比其它函數佔用更多的記憶體。過度使用閉包可能會導致記憶體佔用過多。

  下面我們來看閉包與變數的兩個例子

 1 function createFunctions() { 2   var result = new Array(); 3    4   for (var i = 0; i < 10; i++) { 5     result[i] = function() { 6       return i; 7     } 8   } 9   return result;10 }11 12 var result = createFunctions();13 console.log(result[1]());    // 1014 console.log(result[3]());   // 1015 16 function createFunctionsChanged() {17   var result = new Array();18   19   for (var i = 0; i < 10; i++) {20     result[i] = function(num) {21       return function(){22         return num;23       };24     }(i)25   }26   return result;27 }28 29 var result = createFunctionsChanged();30 console.log(result[1]());   // 131 console.log(result[3]());  // 3

 

  在第二個例子中,我們沒有直接把閉包賦值給數組,而是定義了一個匿名函數,並將立即執行匿名函數的結果賦值給數組。 在調用每個匿名函數時,我們傳入了變數i。由於函數傳值是按值傳遞的,所以就會將變數i的當前值複製給參數num。而在匿名函數的內部,又建立並返回了一個訪問num的閉包。這樣一來,result數組中的每個函數都有自己的num變數的一個副本,也就可以返回不同的數值了。

  更多詳細解釋請參考

  • http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

JavaScript中的閉包

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.