JS中for循環里面的閉包問題的原因及解決辦法


我們先看一個正常的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其他資料來加深印象(百度是基本上搜不到什么有用的文章的)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM