理解閉包,先瞭解一下Javascript變數的作用域。有兩種,全局變數和局部變數。 例子1: a是全局變數,b是局部變數。函數內部可以直接讀取全局變數,但是在函數外部無法讀取函數內部的局部變數。 如何從外部讀取函數內部的局部變數? 例子2: fun()將局部變數b作為返回結果; 例子3: 簡單分析一 ...
理解閉包,先瞭解一下Javascript變數的作用域。有兩種,全局變數和局部變數。
例子1:
<script> var a = 0; function fun(){ var b = 0; console(a+" "+b); } </script>
a是全局變數,b是局部變數。函數內部可以直接讀取全局變數,但是在函數外部無法讀取函數內部的局部變數。
如何從外部讀取函數內部的局部變數?
例子2:
function fun(num){ var b = 0; b += num; return b; } var re_b = fun(1); console.log(re_b); //1 re_b = fun(2); console.log(re_b); //2
fun()將局部變數b作為返回結果;
例子3:
function fun(){ var b = 0; function fun2(num){ b += num; return b; } return fun2 } var re_b = fun(); console.log(re_b(1)); //1 console.log(re_b(2)); //3
簡單分析一下例子3代碼:
定義普通函數fun;
在fun中定義變數b,普通函數fun2(參數),返回fun2;
在fun2中將參數num與b相加後賦予b,返回b;
執行fun,並把返回結果賦予re_b,此時re_b的類型為function;
執行re_b(1),結果輸出1(0+1);
執行re_b(2),結果輸出3(1+2);
上述方法即為閉包。
在瞭解閉包的作用之前,我們先瞭解一下 Javascript 中的 垃圾回收 機制,在 Javascript 中,如果一個對象不再被引用,那麼這個對象就會被 GC 回收,否則這個對象一直會保存在記憶體中。
例子2中,執行完fun,變數b就會被釋放回收;
例子3中,fun2定義在fun中,即fun2依賴於fun,全局變數re_b引用fun2,fun就間接被引用,即fun和fun2與re_b共存亡。只要re_b沒被釋放回收,變數b就一直在記憶體中。這也就是閉包的作用,fun執行完並返回後,閉包使得Javascript的垃圾回收機制GC不會收回fun所占用的資源。
一句話說,當一個內部函數被其外部函數之外的變數引用時,就形成了一個閉包。
閉包的應用場景:保護函數內的變數安全;在記憶體中維持一個變數;通過保護變數的安全實現JS私有屬性和私有方法。
閉包的一個簡單應用:
for(var i=0;i<5;i++){ setTimeout(function(){ console.log(i) },1000*i) }
結果為,每秒鐘輸出一個5,一共輸出5次。
for(var i=0;i<5;i++){ (function fun(i) { setTimeout(function(){ console.log(i) },1000*i) })(i) }
結果為,每秒鐘輸出一個數,0,1,2,3,4。
第一個例子中每次迴圈中的setTimeout回調函數記住的i的值是for迴圈作用域中的值,此時都是5,而第二個例子記住的i的數為setTimeout的父級作用域自執行函數中的i的值,依次為0,1,2,3,4。
註意點:由於閉包會使得函數中的變數都被保存在記憶體中,記憶體消耗很大,濫用閉包,會造成網頁的性能問題,在IE中可能導致記憶體泄露