ECMAScript中經(jīng)常把閉包與匿名函數(shù)混用,所以很多時(shí)候會(huì)搞不清這兩個(gè)概念
閉包是指有權(quán)訪(fǎng)問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)。——《JS高程》
Closures (閉包)是使用被作用域封閉的變量,函數(shù),閉包等執(zhí)行的一個(gè)函數(shù)的作用域。通常我們用和其相應(yīng)的函數(shù)來(lái)指代這些作用域。(可以訪(fǎng)問(wèn)獨(dú)立數(shù)據(jù)的函數(shù))
閉包是一個(gè)函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。從理論角度來(lái)說(shuō),所有函數(shù)都是閉包。
——MDN https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
函數(shù)表達(dá)式
JavaScript中聲明函數(shù)有兩種方式
// 1.函數(shù)聲明
function functionName(arg0, arg1, arg2) {
//函數(shù)體
}
// 2.匿名函數(shù)賦值
var functionName = function(arg0, arg1, arg2){
//函數(shù)體
}
作用域鏈
在函數(shù)執(zhí)行過(guò)程中,需要在作用域鏈中查找變量:
function compare(value1, value2){
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);

當(dāng)函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷(xiāo)毀,內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的變量對(duì)象);
閉包
而閉包有所不同
閉包是一種特殊的對(duì)象。它由兩部分構(gòu)成:函數(shù),以及創(chuàng)建該函數(shù)的環(huán)境。環(huán)境由閉包創(chuàng)建時(shí)在作用域中的任何局部變量組成。
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 compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });
在createComparisonFunction這個(gè)函數(shù)中,返回的匿名函數(shù)賦值給了compare這個(gè)變量,我們可以說(shuō)compare是一個(gè)閉包。
返回的匿名函數(shù)的作用域鏈中可以訪(fǎng)問(wèn)在createComparisonFunction中的所有變量,而且函數(shù)在執(zhí)行完畢后,其活動(dòng)對(duì)象也不會(huì)被銷(xiāo)毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對(duì)象。
直到匿名函數(shù)被銷(xiāo)毀后,createComparisonFunction()的活動(dòng)對(duì)象才會(huì)被銷(xiāo)毀;
//創(chuàng)建函數(shù)
var compareNames = createComparisonFunction("name");
//調(diào)用函數(shù)
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除對(duì)匿名函數(shù)的引用(以便釋放內(nèi)存)
compareNames = null;
首先,創(chuàng)建的比較函數(shù)被保存在變量compareNames 中。而通過(guò)將compareNames 設(shè)置為等于null
解除該函數(shù)的引用,就等于通知垃圾回收例程將其清除。隨著匿名函數(shù)的作用域鏈被銷(xiāo)毀,其他作用域
(除了全局作用域)也都可以安全地銷(xiāo)毀了。

閉包與匿名函數(shù)
在開(kāi)發(fā)中,我們常會(huì)用到這樣的寫(xiě)法
(function () {
// ... all vars and functions are in this scope only
// still maintains access to all globals
}());
這里聲明匿名函數(shù)立即執(zhí)行,使得局部變量不會(huì)污染到全局變量,并可以訪(fǎng)問(wèn)全局變量(外部變量);
var num = 1;
(function () {
var num = 2;
console.log(num); // 2
}())
console.log(num); // 1
匿名函數(shù)通常與閉包一起使用,但并無(wú)必然聯(lián)系;
因?yàn)殚]包保存的是變量對(duì)象,所以我們往往要用匿名函數(shù)立即執(zhí)行來(lái)保存過(guò)程中的值;(見(jiàn)需要注意)
閉包的實(shí)用
想了想,自己總結(jié)的不如直接看文檔。
MDN 文檔 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
- 在函數(shù)中給事件驅(qū)動(dòng)型的變量添加函數(shù);
- 模擬私有方法
需要注意
作用域鏈的這種配置機(jī)制引出了一個(gè)值得注意的副作用,即閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值。 ——《JS高程》
因?yàn)殚]包所保存的是整個(gè)變量對(duì)象;
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
//都是10
我們必須通過(guò)匿名函數(shù)的立即執(zhí)行來(lái)進(jìn)行保存
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
//1-10
當(dāng)然,ES6可以使用let來(lái)替代
function createFunctions(){
var result = new Array();
for (let i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}