我們先看一個正常的for循環,普通函數里面有一個for循環,for循環結束后最終返回結果數組
function box(){ var arr = []; for(var i=0;i<5;i++){ arr[i] = i; } return arr; } alert(box()) //正常情況不需要閉包,就可以達到預期效果,輸出結果為一個數組0,1,2,3,4
有時我們需要在for循環里面添加一個匿名函數來實現更多功能,看下面代碼
//循環里面包含閉包函數 function box(){ var arr = []; for(var i=0;i<5;i++){ arr[i] = function(){ return i; //由於這個閉包的關系,他是循環完畢之后才返回,最終結果是4++是5 } //這個匿名函數里面根本沒有i這個變量,所以匿名函數會從父級函數中去找i, } //當找到這個i的時候,for循環已經循環完畢了,所以最終會返回5 return arr; } //alert(box()); //執行5次匿名函數本身 //alert(box()[1]); //執行第2個匿名函數本身 //alert(box().length); //最終返回的是一個數組,數組的長度為5 alert(box()[0]()); //數組中的第一個數返回的是5,這是為什么?
上面這段代碼就形成了一個閉包:
閉包是指有權訪問另一個函數作用域中的變量的函數,創建閉包的常見的方式,就是在一個函數內部創建另一個函數,通過另一個函數訪問這個函數的局部變量。
在for循環里面的匿名函數執行 return i 語句的時候,由於匿名函數里面沒有i這個變量,所以這個i他要從父級函數中尋找i,而父級函數中的i在for循環中,當找到這個i的時候,是for循環完畢的i,也就是5,所以這個box得到的是一個數組[5,5,5,5,5]。
解決方案1
在看解決方案一之前,我們先看一下匿名函數的自我執行:
匿名函數自我執行的寫法是,在函數體外面加一對圓括號,形成一個表達式,在圓括號后面再加一個圓括號,里面可傳入參數。
例如下代碼:
(function(){ alert('lee'); //匿名函數自我執行(匿名函數)() })();
我們再來看解決方案1:
function box(){ var arr = []; for(var i=0;i<5;i++){ arr[i] = (function(num){ //自我執行,並傳參(將匿名函數形成一個表達式)(傳遞一個參數) return num; //這里的num寫什么都可以 })(i); //這時候這個括號里面的i和上面arr[i]的值是一樣的都是取自for循環里面的i } return arr; } //alert(box()); //alert(box()[1]); //alert(box().length); alert(box()[0]);
通過給匿名函數傳參,而傳遞的這個參數i是每次執行for循環里面的i,每次傳遞的參數i的值都不一樣,匿名函數里面的num接收傳遞的參數i,所以box()最終輸出結果為[0,1,2,3,4]
解決方案2
這種方案的原理就是在匿名函數1里面再寫入一個匿名函數2,這個匿名函數2需要的num值會在他的父級函數匿名函數1里面去尋找,而匿名函數1里面的num值就是傳入的這個參數i,和上面例子中的i是一樣的,
function box(){ var arr = []; for(var i=0;i<5;i++){ arr[i] = (function(num){ //num在這里 //原理和上面一種方法一樣的,所以可以實現閉包 return function(){ //在這個閉包里面再寫一個匿名函數 return num; }; })(i) } return arr; } //alert(box()); //alert(box()[1]); //alert(box().length); var b = box(); alert(b[0]()); alert(box()[0]());
box()最終返回結果[0,1,2,3,4],
解決方案3
如果將一個匿名函數自我執行的時候賦值給一個變量,那么這個匿名函數中的圓括號的可以去掉的,看下面代碼,
var tip = function(){ //這樣把匿名函數自我執行的時候賦值給一個變量,那么圓括號是可以去掉的 alert('lee'); }();
利用匿名函數的這一特點,我們可以將解決方案1中的代碼改進一下:
function box(){ var arr = []; for(var i=0;i<5;i++){ arr[i] = function(num){ return num; }(i); } return arr; } //alert(box()); //alert(box()[1]); //alert(box().length); alert(box()[4]);
匿名函數在執行的時候他本身就傳遞給了一個變量arr[i],所以匿名函數的圓括號是可以去掉的。
以上就是幾種解決for循環中閉包的辦法,當然還有更多辦法,大家可自行google或者bing其他資料來加深印象(百度是基本上搜不到什么有用的文章的)。