閉包 閉包: 指有權訪問另一個函數作用域中的變數的函數。 創建閉包的常見方式就是在一個函數內部創建另一個函數: function createComparisonFunction(propertyName) { return function (obj1, obj2) { // 訪問了外部函數中的變 ...
閉包
閉包:指有權訪問另一個函數作用域中的變數的函數。
創建閉包的常見方式就是在一個函數內部創建另一個函數:
function createComparisonFunction(propertyName) {
return function (obj1, obj2) {
// 訪問了外部函數中的變數
var value1 = obj1[propertyName];
var value2 = obj2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}
上面的obj1、obj2訪問了外部函數的變數propertyName。之所以能訪問到這個變數,是因為內部函數的作用域中包含createComparisonFunction()的作用域。
當某個函數被調用時,會創建一個執行環境及相應的作用域鏈。然後,使用arguments和其他命名參數的值來初始化函數的活動對象。
在函數執行過程中,為讀取和寫入變數的值,就需要在作用域鏈中查找變數。
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
上面先調用了compare()函數,然後又在全局作用域調用了它,當調用compare()時,會創建一個包含arguments、value1和value2的活動對象。全局執行環境的變數對象在compare()執行環境的作用域鏈中則處於第二位。
compare()函數執行時的作用域鏈:
在另一個函數內部定義的函數會將包含函數(即外部函數)的活動對象添加到它的作用域鏈中。因此createComparisonFunction()函數內部定義的匿名內部函數的作用域鏈中,實際上將會包含外部函數createComparisonFunction()的活動對象。
var compare = createComparisonFunction("name");
var result = compare({name: "Nicholas"}, {name: "Greg"});
下圖展示了代碼執行時,包含函數與內部匿名函數的作用域鏈。
匿名函數從createComparisonFunction()中被返回後,它的作用域鏈被初始化為包含createComparisonFunction()函數的活動對象和全局變數對象。這樣,匿名函數就可以訪問在createComparisonFunction()中定義的所有變數。另外,在createComparisonFunction()執行完,它的活動對象不會被銷毀,從圖中可以看出,匿名函數還引用了這個活動對象。當createComparisonFunction()函數返回時,其執行環境的作用域鏈會被銷毀,但它的活動對象仍會留在記憶體中;匿名函數被銷毀後,createComparisonFunction()的活動對象才會銷毀。
// 創建函數
var compareNames = createComparisonFunction("name");
// 調用函數
var result = compareNames({name: "Nicholas"}, {name: "Greg"});
// 解除對匿名函數的引用(釋放記憶體)
compareNames = null;
首先,創建的比較函數被保存在compareNames,將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。
通過創建另一個匿名函數強制讓閉包的行為符合預期,如下:
function createFunctions() {
for (var i = 0; i < 10; i++) {
return [i] = function (num) {
return function () {
return num;
};
}(i);
}
return result;
}
this對象
this對象在運行時是基於函數的執行環境進行綁定的,在全局函數中,this等於window;當某個對象的方法調用時,this等於那個對象。但是,匿名函數的執行環境具有全局性,其this對象指向window。
看一個例子:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
alert(object.getNameFunc()()); // "The Window"
上面的例子中首先創建了一個全局變數name,又創建了一個包含name屬性的對象。這個對象包含一個方法:getNameFunc(),它返回一個匿名函數,而匿名函數又返回this.name。由於getNameFunc()返回一個函數,因此調用object.getNameFunc()()就會調用它返回的函數,結果為:this.name。然而,返回的字元串是"The Window"。
為什麼匿名函數沒有取得其包含作用域的this對象呢?
每個函數在函數在被調用時都會獲取兩個變數:this和arguments。內部函數在搜索這兩個變數時,只會搜索到其活動對象為止,不可能訪問到外部函數中的這兩個變數。
把外部作用域中的this對象保存到一個閉包能夠訪問到的變數里,就可以讓閉包訪問該對象了。
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
var that = this;
return function () {
return that.name;
};
}
};
alert(object.getNameFunc()()); // "The Window"