1. JS中變量的作用域
在理解閉包之前遭铺,我們得弄清楚JS中變量的作用域原理,它分為全局作用域和局部作用域恢准,它有一個(gè)特點(diǎn)就是局部可以獲取全局的聲明變量魂挂,而全局卻不能得到局部聲明的變量,我們先來看一個(gè)小例子:
var num = 1902;
function methods() {
var qty = 25;
console.log(num);
}
methods(); // 1902
console.log(qty); // 報(bào)錯(cuò)馁筐,找不到 qty 變量;
當(dāng)然在局部聲明變量的時(shí)候一定要用var或者let涂召,不然會(huì)在全局生成一個(gè)變量,容易照成全局污染敏沉,上面代碼如果qty沒有var聲明:
var num = 1902;
function methods() {
qty = 25;
console.log(num);
}
methods(); // 1902;
console.log(qty); // 25;
2.什么是閉包
那么現(xiàn)在問題來了芹扭,如果我們非要從外部來讀取局部變量中的聲明變量呢,尋常方式不行赦抖,我們可以變通一下舱卡,就是在函數(shù)內(nèi)部再嵌套一個(gè)函數(shù),然后返回這個(gè)嵌套函數(shù):
function methods() {
var qty = 25;
return function num() {
console.log(qty);
}
}
var num1 = methods();
num1() // 25
這樣队萤,控制臺(tái)就會(huì)打印出 qty 變量的值了轮锥,其實(shí)在上面的代碼中,被返回的函數(shù) num()就產(chǎn)生了閉包要尔,由于在js中舍杜,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,所以可以把閉包理解成定義在一個(gè)函數(shù)內(nèi)部的函數(shù)赵辕,簡(jiǎn)單的說既绩,JavaScript允許使用內(nèi)部函數(shù):即函數(shù)定義和函數(shù)表達(dá)式位于另一個(gè)函數(shù)的函數(shù)體內(nèi)而且,這些內(nèi)部函數(shù)可以訪問它們所在的外部函數(shù)中聲明的所有局部變量还惠、參數(shù)和聲明的其他內(nèi)部函數(shù)饲握。當(dāng)其中一個(gè)這樣的內(nèi)部函數(shù)在包含它們的外部函數(shù)之外被調(diào)用時(shí),就會(huì)形成閉包蚕键。
3.閉包的用途
我們接著探討閉包的表達(dá)形式以及用途救欧。
(1)匿名自執(zhí)行函數(shù)
(function () {
var methods = function () {
console.log('執(zhí)行完函數(shù)后銷毀')
};
methods();
})();
上面代碼也是閉包的應(yīng)用,運(yùn)用于函數(shù)只會(huì)執(zhí)行一次的場(chǎng)景锣光,執(zhí)行完便會(huì)被釋放笆怠。
(2)給對(duì)象設(shè)置私有變量
var result = function () {
var count = 1;
return function () {
count++;
console.log(count)
}
}()
result(); // 2
result(); // 3
result(); // 4
result(); // 5
上面代碼可以保存自己的私有變量,防止代碼之間的沖突誊爹。
(3)異步執(zhí)行函數(shù)
下面先看一個(gè)小例子:
for (var i = 0; i < 5; i++) {
console.log(i); //0蹬刷,1瓢捉,2,3办成,4
}
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); //5泊柬,5,5诈火,5兽赁,5
}, 0)
}
為什么會(huì)出現(xiàn)上述差異呢,原因在于setTimeout是異步加載冷守,所以為先循環(huán)結(jié)束后輸出最后結(jié)果刀崖,如果我們就是想實(shí)現(xiàn)輸出0,1拍摇,2亮钦,3,4呢充活。那就要用到閉包了:
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(() => {
console.log(i); //0蜂莉,1,2混卵,3映穗,4
}, 0);
})(i);
}
上面就是異步調(diào)用閉包,它可以讓變量值始終保存在內(nèi)存中幕随,即使外部的執(zhí)行環(huán)境已經(jīng)結(jié)束了蚁滋。
4.閉包的優(yōu)缺點(diǎn)
(1)由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大赘淮,所以不能濫用閉包辕录,否則會(huì)造成網(wǎng)頁(yè)的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露梢卸。解決方法是走诞,在退出函數(shù)之前,將不使用的局部變量全部刪除蛤高。
(2)閉包會(huì)在父函數(shù)外部蚣旱,改變父函數(shù)內(nèi)部變量的值。所以襟齿,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用姻锁,把閉包當(dāng)作它的公用方法(Public Method)枕赵,把內(nèi)部變量當(dāng)作它的私有屬性(private value)猜欺,這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值拷窜。