什麼是閉包?? 有權訪問另一個函數作用域內變數的函數就是閉包。 看一個例子: 在這裡,控制台兩次輸出不一樣???為什麼??變數n不是重新賦值了嗎?? 並沒有,在這裡,var c = a(); 這段代碼意思是將 a 方法的返回值賦值給變數 c ,那麼 a() 的返回值就是 inc ,也就 functi ...
什麼是閉包??
有權訪問另一個函數作用域內變數的函數就是閉包。
看一個例子:
1 function a(){ 2 var n = 0; 3 function inc(){ 4 n++; 5 console.log(n); 6 } 7 return inc; 8 } 9 var c = a(); 10 c(); //控制台輸出1 11 c(); //控制台輸出2
在這裡,控制台兩次輸出不一樣???為什麼??變數n不是重新賦值了嗎??
並沒有,在這裡,var c = a(); 這段代碼意思是將 a 方法的返回值賦值給變數 c ,那麼 a() 的返回值就是 inc ,也就 function inc() ,
後面執行兩次 c(); c(); 實際上是執行兩次的 inc(); 因為function一直引用著a(),所以n沒有回收,也就是說,n的值一直在用著,
自然第一次執行 c() 列印1,第二次執行 c() 列印2;
再看一個經典例子:
1 function createFunctions(){ 2 var result = []; 3 for (var i=0; i < 10; i++){ 4 result[i] = function(){ 5 return i; 6 }; 7 } 8 return result; 9 } 10 var funcs = createFunctions(); 11 for (var i=0; i < funcs.length; i++){ 12 console.log(funcs[i]()); 13 }
一般來說,以為輸出 0~9 ,但事實上是輸出10個10。
需要註意的點就是,方法帶()才是執行這個方法,而諸如 var abc = function(){ },只能說將這個方法定義並且賦值給abc,並沒有執行這個方法。
簡單來說:單純的一句 var f = function() { alert('lalala'); }; 是不會彈窗的,後面接一句 f(); 才會執行方法內部的代碼,才會彈窗。
以上代碼的解釋:
1 function createFunctions(){ 2 var result = new Array(); 3 for (var i=0; i < 10; i++){ 4 result[i] = function(){ 5 return i; 6 }; 7 } 8 return result; 9 } 10 var funcs = createFunctions(); //執行了一次createFunctions() 11 for (var i=0; i < funcs.length; i++){ 12 console.log(funcs[i]()); //createFunctions的返回值是result,所以funcs[i]() 執行的是 result[i] 的方法:return i; 13 }
執行流程:
1 var result =[], i; 2 result[0] = function(){ return i; }; //沒執行函數,函數內部不變,不能將函數內的i替換!這是在將createFunction()賦值給funcs時的第一次執行這個方法 3 result[1] = function(){ return i; }; //沒執行函數,函數內部不變,不能將函數內的i替換! 4 ... 5 result[9] = function(){ return i; }; //沒執行函數,函數內部不變,不能將函數內的i替換! 6 i = 10; //此時的i = 10; 7 funcs = result; //因為createFuntions()的返回值是result 8 result = null; 9 10 console.log(i); // funcs[0]()就是執行 return i 語句,就是返回10 11 console.log(i); // funcs[1]()就是執行 return i 語句,就是返回10 12 ... 13 console.log(i); // funcs[9]()就是執行 return i 語句,就是返回10
因為正在引用 i ,所以 i 的值是10,也就是 return i ; 的值一直是return 10 。 傳遞到執行 funcs[i]() 方法裡面。
funcs[0]()就是執行 return i 語句,就是返回10
funcs[1]()就是執行 return i 語句,就是返回10
funcs[9]()就是執行 return i 語句,就是返回10
閉包就是一個函數引用另外一個函數的變數,因為變數被引用著所以不會被回收,因此可以用來封裝一個私有變數。這是優點也是缺點,不必要的閉包只會徒增記憶體消耗!另外使用閉包也要註意變數的值是否符合你的要求,因為他就像一個靜態私有變數一樣。閉包通常會跟很多東西混搭起來,接觸多了才能加深理解,這裡只是說說基礎性的東西。
一道經典題:
1 for (var i = 0; i < 5; i++) { 2 setTimeout(function() { 3 console.log(i); 4 }, 1000 * i); 5 }
setTimeout 會延遲執行,那麼執行到 console.log 的時候,其實 i 已經變成 5 了。
上面代碼一開始輸出一個 5,然後每隔一秒再輸出一個 5,一共 5 個 5。
1 for (var i = 0; i < 5; i++) { 2 (function(i) { 3 setTimeout(function() { 4 console.log(i); 5 }, i * 1000); 6 })(i); 7 }
加上閉包之後,就是每隔一秒輸出0到4,一開始是輸出0,在這裡,內部方法引用了外部 i 局部變數,所以 i 的值傳遞到內部;
如果把 i 刪掉之後:
1 for (var i = 0; i < 5; i++) { 2 (function() { 3 setTimeout(function() { 4 console.log(i); 5 }, i * 1000); 6 })(i); 7 }
這樣子的話,內部其實沒有對 i 保持引用,其實會變成每隔一秒輸出 5,一開始輸出5,共5個5
再如果改成這樣:
1 for (var i = 0; i < 5; i++) { 2 setTimeout((function(i) { 3 console.log(i); 4 })(i), i * 1000); 5 }
這裡給 setTimeout 傳遞了一個立即執行函數。setTimeout 可以接受函數或者字元串作為參數,那麼這裡立即執行函數應該是個 undefined ,也就是說等價於:setTimeout(undefined, ...);
而立即執行函數會立即執行,那麼應該是立馬輸出的。
也就是立馬輸出 0 到 4 。