for循环内使用闭包,变量的作用域详解

时间:2022-04-20 22:45:45
循环内使用闭包的其实要根据作用域来解释

首先js中是没有“块状作用域”的,请看下面java例子
Java code ?
1
2
3
4
5
6
7
8
      //scope1 作用域1
      for  ( int  i =  0 ; i <  10 ; i++){
         //scope2 作用域2
          String str =  "123" ;
      
      }     
      Systen.out.print(i);
      Systen.out.print(str);

  
在有块状作用域的语言中,scope1与scope2是不同的作用域,所以
Systen.out.print(i);与Systen.out.print(str);都应该报错,i,str未定义
  
但js并没有块状作用域,看以下例子
JavaScript code ?
1
2
3
4
5
6
7
8
//scope1 作用域1
      for  ( var  i = 0; i < 10; i++){
          //scope2 作用域2
          var  str =  "123" ;
      }
           
      alert(i);
      alert(str);


在js中scope1与scope2是同级的,所以alert(i)打印出10, alert(str)打印出123
不单for是这种情况,if,while等都是一样的,

上面例子等同于

JavaScript code ?
1
2
3
4
5
6
7
8
9
10
//scope1 作用域1
      var  i = 0;
      var  str;
      for  (; i < 10; i++){
          //scope2 作用域2
          str =  "123" ;
      }
           
      alert(i);
      alert(str);

  
所以
JavaScript code ?
1
2
3
4
5
6
7
8
9
10
//scope1 作用域1
      var  els = document.getElementsByTagName( "li" );
      for  ( var  i = 0, j = els.length; i < j; i++){
          //scope2 作用域2
          var  el = els[i];
         el.onclick =  function (){
             //scope3 作用域3
             alert(i);
         }
      }



其实等同于

JavaScript code ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//scope1 作用域1
      var  els = document.getElementsByTagName( "li" );
      var  i = 0, j = els.length;
 
      for  (; i < j; i++){
          //scope2 作用域2
          var  el = els[i];
         el.onclick = fn;
      }
 
      function  fn(){
         //scope3 作用域3
         alert(i);  //当执行onclick时,i已经循环完毕
      }


这时大家会想,scope3与scope1,scope2是不同一个作用域,为什么还能打印出来,
这是因为js有作用域链这种思想,当i在scope3找不到的时候,会向上级作用域scope1访问,如果类推,
直到scope1访问到,所以这里的i其实就是scope1中的i,这时i已经是循环完毕了。


因此要避免这种情况,我们就要从它的作用域入手了,请看改造例子

JavaScript code ?
1
2
3
4
5
6
7
8
9
10
11
12
13
//scope1 作用域1
      var  els = document.getElementsByTagName( "li" );
      for  ( var  i = 0, j = els.length; i < j; i++){
          //scope2 作用域2
          var  el = els[i];
         el.onclick =  function (i){
             //scope3 作用域3
             return  function (){
                 //scope4 作用域4
                 alert(i)
             }
         }(i)
      }


因为作用域链,alert(i)访问scope4没有,向上级scope3访问,它发现i已经由(i)传进来赋值了,
所以alert(i)这里的i其实是scope3作用域中的i,而并不是scope1中的i

而如果我们改成这样
JavaScript code ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//scope1 作用域1
      var  els = document.getElementsByTagName( "li" );
      for  ( var  i = 0, j = els.length; i < j; i++){
          //scope2 作用域2
          var  el = els[i];
         
         el.onclick =  function (){  //这里没有声明形参
             //scope3 作用域3
             return  function (){
                 //scope4 作用域4
                 alert(i)
             }
         }(i)
      }


alert(i)中的i访问到的就是scope1的i,打印出来的还是循环完毕后的i...