閉包
函數(shù)和對其周圍狀態(tài)(lexical environment,詞法環(huán)境)的引用捆綁在一起構(gòu)成閉包(closure)。也就是說,閉包可以讓你從內(nèi)部函數(shù)訪問外部函數(shù)作用域。在 JavaScript 中,每當(dāng)函數(shù)被創(chuàng)建,就會(huì)在函數(shù)生成時(shí)生成閉包。
由于閉包的概念比較抽象,所以本篇幅會(huì)有較多的主觀理解。
在作用域相關(guān)的內(nèi)容中可以知道,全局下的作用域想訪問一個(gè)函數(shù)內(nèi)部的作用域是辦不到的,但是 閉包
的特性可以突破這一限制。
每個(gè)函數(shù)都會(huì)形成一個(gè)閉包。
1. 什么是閉包
閉包可以理解成,保留了函數(shù)作用域鏈的一個(gè)環(huán)境。
var fn = function() {
var number = 0;
};
fn();
console.log(number);
這個(gè)例子是訪問不到 number
的,想訪問到就可以借助閉包的特性。
var fn = function() {
var number = 0;
return function() {
number++;
console.log(number);
};
};
var increment = fn();
increment();
increment();
increment();
這里的 fn
函數(shù)返回了一個(gè)函數(shù),在這個(gè)返回的函數(shù)所形成的閉包環(huán)境就擁有訪問上一層作用域的能力,所以每次在調(diào)用 fn
返回的函數(shù)時(shí),就可以累加 number
。
借助一個(gè)函數(shù)形成的閉包環(huán)境作為跳板,來訪問另一個(gè)函數(shù)的作用域,就是閉包最常見的使用場景。
網(wǎng)絡(luò)上有許多文獻(xiàn)把閉包稱為能訪問其他函數(shù)內(nèi)部變量的函數(shù)
,這樣理解可能更容易一些。
函數(shù)用到了上層作用域的變量,所以這些變量會(huì)在內(nèi)存中被保留,不會(huì)被釋放,一些舊的瀏覽器在內(nèi)存管理上沒有現(xiàn)代瀏覽器完善,大量的閉包可能會(huì)導(dǎo)致頁面卡頓,不過通常業(yè)務(wù)開發(fā),會(huì)先考慮效果,再考慮性能。
2. 閉包的應(yīng)用
2.1 模擬私有屬性
在 JavaScript
中是沒有私有屬性特性的,利用閉包來隱藏變量,就可以模擬出私有屬性的效果。
var counter = (function() {
var count = 0;
return {
increment: function() {
count++;
return count;
},
zero: function() {
count = 0;
return count;
},
get value() {
return count;
},
};
})();
counter.increment();
console.log(counter.value); // 輸出:1
counter.increment();
console.log(counter.value); // 輸出:2
console.log(counter.count); // 輸出:undefined
這里的自執(zhí)行匿名函數(shù)返回一個(gè)對象,對象中的方法就具有訪問上層函數(shù)中的變量的能力,所以他們都能訪問 count。
因?yàn)?count 不會(huì)被釋放,所以可以當(dāng)作一個(gè)屬性來使用。
2.2 回調(diào)函數(shù)幾乎都用到了閉包的特性
回調(diào)函數(shù)通常會(huì)用到上層作用域的變量,然后在某一情況下進(jìn)行調(diào)用。
var fn = function(cb) {
console.log('異步操作開始');
setTimeout(function() {
console.log('異步操作結(jié)束');
cb();
}, 1000);
};
var obj = {
flag: false,
};
fn(function() {
obj.flag = true;
console.log(obj);
});
很明顯,這里的回調(diào)函數(shù)就是用到了閉包的特性。
所以閉包其實(shí)很常用,結(jié)合日常的這些場景能更好的理解閉包。
3. 小結(jié)
每個(gè)函數(shù)都有閉包,閉包可以訪問到這個(gè)函數(shù)所在的上層作用域,利用這一特性,就能訪問到一個(gè)函數(shù)作用域下的變量。
大量的閉包可能會(huì)造成性能問題,不過現(xiàn)在的計(jì)算機(jī)處理器、內(nèi)存已經(jīng)讓開發(fā)者不太需要關(guān)注這方面的問題,但在設(shè)計(jì)一個(gè)會(huì)被大量應(yīng)用的庫和框架時(shí),應(yīng)當(dāng)做這方面的考慮,因?yàn)橛脩舻沫h(huán)境千變?nèi)f化。