定義一個(gè)函數(shù)
<pre>
function fn(a,b,...rest){
var arr;
...
function fn2(){
引用外部函數(shù)fn的參數(shù)a,b...rest和局部變量arr
};
return fn2;
}
fn();
</pre>
當(dāng)調(diào)用fn ()這個(gè)函數(shù)時(shí),返回的是fn2()這個(gè)函數(shù),fn()的相關(guān)參數(shù)和變量都保存在返回的函數(shù)fn2()中,這種結(jié)構(gòu)就稱為閉包。閉包就是函數(shù)fn2(),即能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
a,b...rest,arr這些局部變量在fn()這個(gè)函數(shù)外是無(wú)法被讀取的,但是有時(shí)候又想要讀取這些變量,怎么辦呢?
在fn的內(nèi)部再定義一個(gè)函數(shù)fn2(),fn2()是可以訪問(wèn)fn內(nèi)部所有的局部變量的,在內(nèi)部引用局部變量arr,將fn2()作為返回值,即可在外面調(diào)用fn的局部變量。
<pre>
function fn() {
var n = 999;
function fn2() {
console.log(n);
}
return f2;
}
var result = fn();
result(); //999
</pre>
調(diào)用fn()時(shí),返回的并不是n的值,而是函數(shù)fn2(),因此我門要在外面將fn賦給一個(gè)函數(shù)result,利用調(diào)用result();返回的值才是n的值。返回的函數(shù)并沒(méi)有立刻執(zhí)行,而是直到調(diào)用了result()才執(zhí)行。
另一個(gè)例子
<pre>
function lazy_sum(arr) {
var sum = function () {
return arr.reduce(function (x, y) {
return x + y;
});
}
return sum;
}
var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()
f(); // 15
</pre>
同樣的,當(dāng)我們調(diào)用lazy_sum()時(shí),返回的并不是求和結(jié)果,而是求和函數(shù)。調(diào)用函數(shù)f
時(shí),才真正計(jì)算求和的結(jié)果。
當(dāng)我們調(diào)用lazy_sum()時(shí),每次調(diào)用都會(huì)返回一個(gè)新的函數(shù),即使傳入相同的參數(shù):
<pre>var f1 = lazy_sum([1, 2, 3, 4, 5]);
var f2 = lazy_sum([1, 2, 3, 4, 5]);
f1 === f2; // false
</pre>
f1()和f2()的調(diào)用結(jié)果互不影響。
于在JavaScript語(yǔ)言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取內(nèi)部變量,因此可以把閉包簡(jiǎn)單理解成“定義在一個(gè)函數(shù)內(nèi)部的函數(shù)”。閉包最大的特點(diǎn),就是它可以“記住”誕生的環(huán)境,比如fn2記住了它誕生的環(huán)境fn,所以從fn2可以得到fn的內(nèi)部變量。在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來(lái)的一座橋梁。
閉包的最大用處有兩個(gè),一個(gè)是可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量始終保持在內(nèi)存中,即閉包可以使得它誕生環(huán)境一直存在。
內(nèi)存泄露
因?yàn)閞esult();一直存在在內(nèi)存中,而result()的存在依賴于fn,因此也始終在內(nèi)存中,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制回收,內(nèi)存一直得不到釋放,JS釋放內(nèi)存時(shí)就會(huì)漏掉這一部分,漸漸越積越多,造成內(nèi)存泄露。
返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量。