天天看點

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循環裡面添加一個匿名函數來實作更多功能,看下面代碼

//循環裡面包含閉包函數

        arr[i] = function(){

            return i;         //由于這個閉包的關系,他是循環完畢之後才傳回,最終結果是4++是5

        }                    //這個匿名函數裡面根本沒有i這個變量,是以匿名函數會從父級函數中去找i,

    }                      //當找到這個i的時候,for循環已經循環完畢了,是以最終會傳回5

//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]。

解決方案

在看解決方案一之前,我們先看一下匿名函數的自我執行:

匿名函數自我執行的寫法是,在函數體外面加一對圓括号,形成一個表達式,在圓括号後面再加一個圓括号,裡面可傳入參數。

例如下代碼:

(function(){

    alert('lee');     //匿名函數自我執行(匿名函數)()

})();

解決方案1:

        arr[i] = (function(num){        //自我執行,并傳參(将匿名函數形成一個表達式)(傳遞一個參數)

          return num;        //這裡的num寫什麼都可以                   

      })(i);        //這時候這個括号裡面的i和上面arr[i]的值是一樣的都是取自for循環裡面的i                           

    }                                           

//alert(box());                               

//alert(box()[1]);

//alert(box().length);                           

alert(box()[0]);       

解決方案2

這種方案的原理就是在匿名函數1裡面再寫入一個匿名函數2,這個匿名函數2需要的num值會在他的父級函數匿名函數1裡面去尋找,而匿名函數1裡面的num值就是傳入的這個參數i,和上面例子中的i是一樣的,

        arr[i] = (function(num){

        //num在這裡             //原理和上面一種方法一樣的,是以可以實作閉包                   

        return function(){      //在這個閉包裡面再寫一個匿名函數

                return num;                           

            };                                                                

        })(i)                                               

//alert(box().length);

var b = box();                           

alert(b[0]());

alert(box()[0]());

box()最終傳回結果[0,1,2,3,4],

案例詳解

<html >  

<head>  

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  

<title>閉包示範</title>  

<script type="text/javascript">  

function init() {  

  var pAry = document.getElementsByTagName("p");  

  for( var i=0; i<pAry.length; i++ ) {  

     pAry[i].onclick = function() {  

     alert(i);  

  }  

 }  

}  

</script>  

</head>  

<body onload="init();">  

<p>産品一</p>  

<p>産品二</p>  

<p>産品三</p>  

<p>産品四</p>  

<p>産品五</p>  

</body>  

</html>

解決方式有以下幾種

1、将變量 i 儲存給在每個段落對象(p)上

 var pAry = document.getElementsByTagName("p");  

 for( var i=0; i<pAry.length; i++ ) {  

   pAry[i].i = i;  

   pAry[i].onclick = function() {  

    alert(this.i);  

   }  

2、加一層閉包,i以函數參數形式傳遞給内層函數

function init3() {  

  (function(arg){    

    pAry[i].onclick = function() {    

     alert(arg);  

    };  

  })(i);//調用時參數  

3、加一層閉包,i以局部變量形式傳遞給記憶體函數

function init4() {  

 for( var i=0; i<pAry.length; i++ ) {   

  (function () {  

   var temp = i;//調用時局部變量  

   pAry[i].onclick = function() {   

    alert(temp);   

  })();  

繼續閱讀