定義一個函數(shù)
<pre>
function fn(a,b,...rest){
var arr;
...
function fn2(){
引用外部函數(shù)fn的參數(shù)a,b...rest和局部變量arr
};
return fn2;
}
fn();
</pre>
當調(diào)用fn ()這個函數(shù)時叮雳,返回的是fn2()這個函數(shù),fn()的相關(guān)參數(shù)和變量都保存在返回的函數(shù)fn2()中,這種結(jié)構(gòu)就稱為閉包己肮。閉包就是函數(shù)fn2()沫屡,即能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)蛤吓。
a,b...rest,arr這些局部變量在fn()這個函數(shù)外是無法被讀取的披坏,但是有時候又想要讀取這些變量眶根,怎么辦呢逊桦?
在fn的內(nèi)部再定義一個函數(shù)fn2()谈喳,fn2()是可以訪問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()時巴席,返回的并不是n的值历涝,而是函數(shù)fn2(),因此我門要在外面將fn賦給一個函數(shù)result,利用調(diào)用result()漾唉;返回的值才是n的值荧库。返回的函數(shù)并沒有立刻執(zhí)行,而是直到調(diào)用了result()才執(zhí)行赵刑。
另一個例子
<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>
同樣的分衫,當我們調(diào)用lazy_sum()時,返回的并不是求和結(jié)果般此,而是求和函數(shù)蚪战。調(diào)用函數(shù)f
時,才真正計算求和的結(jié)果铐懊。
當我們調(diào)用lazy_sum()時邀桑,每次調(diào)用都會返回一個新的函數(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語言中壁畸,只有函數(shù)內(nèi)部的子函數(shù)才能讀取內(nèi)部變量,因此可以把閉包簡單理解成“定義在一個函數(shù)內(nèi)部的函數(shù)”茅茂。閉包最大的特點捏萍,就是它可以“記住”誕生的環(huán)境,比如fn2記住了它誕生的環(huán)境fn玉吁,所以從fn2可以得到fn的內(nèi)部變量照弥。在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁进副。
閉包的最大用處有兩個这揣,一個是可以讀取函數(shù)內(nèi)部的變量,另一個就是讓這些變量始終保持在內(nèi)存中影斑,即閉包可以使得它誕生環(huán)境一直存在给赞。
內(nèi)存泄露
因為result();一直存在在內(nèi)存中,而result()的存在依賴于fn矫户,因此也始終在內(nèi)存中片迅,不會在調(diào)用結(jié)束后,被垃圾回收機制回收皆辽,內(nèi)存一直得不到釋放柑蛇,JS釋放內(nèi)存時就會漏掉這一部分芥挣,漸漸越積越多,造成內(nèi)存泄露耻台。
返回函數(shù)不要引用任何循環(huán)變量空免,或者后續(xù)會發(fā)生變化的變量。