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