原文連接----請點這裏javascript
閉包是指有權訪問另外一個函數做用域中的變量的函數,建立閉包的常見方式,就是在一個函數內部建立另外一個函數。
之因此一個內部的函數能夠訪問其外部的變量,並且在其被返回或是調用時還能夠訪問,是由於這個內部函數的做用域鏈中包含外部函數的做用域。前端
在瞭解閉包以前,先要熟悉如下幾點:
1. 首先要理解執行環境,執行環境定義了變量或函數有權訪問的其餘數據。
2. 每一個執行環境都有一個與之關聯的變量對象,環境中定義的全部變量和函數都保存在這個對象中。
3. 每一個函數都有本身的執行環境,當執行流進入一個函數時,函數的環境就會被推入到一個環境棧中。而在函數執行以後,棧將其環境彈出,把控制權返回給以前的執行環境。<!-- more -->
4. 當某個函數被調用時,會建立一個執行環境及其相應的做用域鏈。而後使用arguments
和其餘命名參數的值來初始化函數的活動對象。在函數中,活動對象做爲變量對象使用(做用域鏈是由每層的變量對象鏈起來的)。
5. 在做用域鏈中,外部函數的活動對象始終處於第二位,外部函數的外部函數的活動對象處於第三位,直到做用域鏈終點即全局執行環境。
6. 做用域鏈的本質是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。java
不談論閉包,通常的,從在全局執行環境建立一個函數開始。
在建立一個函數時,會建立一個預先包含全局變量對象的做用域鏈,這個做用域鏈被保存在函數內部的[[Scope]]
。
而後執行流進入這個函數,函數的執行環境被壓入環境棧中,此函數執行環境的活動對象做爲變量對象被建立並推入執行環境做用域鏈的前端。
對這個例子中的函數而言,其做用域鏈中包含兩個變量對象:本地活動對象和全局變量對象。
不管在何時在函數中訪問變量時,會從做用域鏈搜索變量名。
通常狀況下,函數執行完,局部活動對象就會被銷燬,內存中僅有全局做用域(裏邊只有全局執行環境的變量對象)。
如下面這段代碼爲例:安全
function compare (value1, value2) { //建立一個預先包含全局變量對象的做用域鏈,保存在[[Scope]] if (value1 < value2) { //訪問函數變量時,即在代碼最後一條語句執行過程當中,會從做用域鏈前端開始搜索變量名 return -1; } else if (value1 > value2) { return 1; } else { return 0; } } var result = compare(5, 10); //執行流進入函數時,compare的執行環境壓入環境棧 //compare執行環境的活動對象做爲變量對象接到做用域鏈的前端 //函數執行完,compare執行環境彈出棧,compare活動對象銷燬
如圖,做用域鏈從0開始向後查找:閉包
以下是一個以屬性名做爲參數,按其屬性的值對數據進行排序的函數:函數
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; } }; } var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}]; data.sort(createComparisonFunction("name")); console.log(data[0]); //Object {name: "Nicholas", age: 29} data.sort(createComparisonFunction("age")); console.log(data[0]); //Object {name: "Zachary", age: 28}
createComparisonFunction()
函數和返回的匿名函數的做用域鏈以下圖所示: spa
在匿名函數從
createComparisonFunction()
中被返回後,它的做用域鏈被初始化爲包含createComparisonFunction()
函數的活動對象和全局變量對象。這樣,匿名函數就能夠訪問在createComparisonFunction()
中定義的全部變量。指針
更爲重要的是:code
createComparisonFunction()
函數在執行完畢後,其餘活動對象也不會被銷燬,由於匿名函數的做用域鏈仍然在引用這個活動對象。對象
當createComparisonFunction()
函數返回後,其執行環境的做用域鏈會被銷燬,但它的活動對象仍然會留在內存中;直到匿名函數被銷燬,createComparisonFunction()
的活動對象纔會被銷燬。
例如如下代碼,返回的匿名函數被保存在變量compareNames
中,經過將compareNames
設置爲null
來解除對匿名函數的引用,解除引用以後垃圾回收例程將會清除該匿名函數,隨之該匿名函數的做用域鏈也會被銷燬,則其做用域鏈上的其餘做用域也會安全的銷燬(全局做用域除外)。
var compareNames = createComparisonFunction("name"); var result = compareNames({ name: "Nicholas" }, { name: "Greg" }); compareNames = null;