点击第0个<li></li>弹出0,点击第一个<li></li>弹出1

最近遇到了一个问题,一个<ul></ul>里面嵌套了好多空的<li>标签,点击<li></li>弹出它的序号(HTML不可修改)。即点击第0个<li>标签弹出0(作为一个程序猿,我们就从第0个开始说起吧),点击第1个<li>标签弹出1。给出的HTML结构如下:

<ul id=”ul1”>
   <li></li>
   <li></li>
   <li></li>
</ul>
 

先写一段错误的代码:

 
var ul1 = document.getElementById(“ul1”);
var oli =ul1.getElementsByTagName(“li”);
      for(var i = 0;i<oli.length;i++){
        oli[i].onclick =function(){
          alert(i);
      }
      }

相信每一个初学者都会写出这样的代码,然后一运行,怎么每一个li弹出的都是3。好,我们分析一下代码,for循环我们遍历ul1里面的每一个li,并且给他注册了一个click事件,每一个li被点击的时候都应该弹出它的序号i,然而事实是当你点击li的时候,for循环早已经执行完,给每一个li注册了事件,那么问题来了,此时的for循环已经遍历完了,i值已经变成了li.length。所以每次点击li都弹出了3.

 

然后百度一下,给出了这样的一段代码:

for(var i =0;i<oli.length;i++){
        oli[i].onclick = (function(i){
          return function(){
            alert(i);
          }
        })(i);
      }

咋一看,这到底为啥对了呢?

我们来分析一下代码,for循环遍历每一个li并且给他们注册一个click事件,(function(){ })()  这个玩意看不懂的自行百度,就是会立即执行的函数,也就是说在给每一个li注册click事件的时候,这个立即执行的函数就被执行了。以第0个为例,给li[0].click 赋值的时候,遇到一个立即执行的函数,好的,执行,执行完了这个函数的返回值是一个函数把他赋值给li[0].click,所以其实这时候给li[0]注册的点击事件是return的那个匿名函数。这里就用到了闭包,或许我写成这样会更容易理解:

 

for(var i =0;i<oli.length;i++){
        oli[i].onclick = (function(i){
         function f(){
          alert(i);
         }
         return f;
        })(i);
      }

在即时函数里面定义了一个内部函数,这个内部函数可以访问到外部函数里面的i值,而return f,这条语句相当于把f函数的引用作为返回值赋值给oli[0]的click事件。这时候f函数里面的i值会一直在内存中,不会释放掉。好。当oli[0]触发click事件,f函数就会被执行。

同理,给oli[1]注册事件的时候,同样会有一个f函数不会释放掉,因为他的引用被赋值给oli[1].onclick。每一个li的i值相互独立。

可以用下面这段代码进行测试一下:

for(var i =0;i<oli.length;i++){
        oli[i].onclick = (function(i){
         return function (){
          alert(i++);
       }
        })(i);
      }

运行结果:点击第0个li标签,弹出0,再次点击,弹出1,再次点击弹出2;然后你再去点击第1个li标签,弹出1。

***所以这里的i值是传值,每一个i有自己独立的内存空间,互不影响。再看一个这样的一段代码:

var aa = {};   
    for(aa.i = 0;aa.i<len;aa.i++){
      setTimeout((function(aa){
      return function(){
        alert(aa.i);
      }
     })(aa),200);
}

这段代码里的i 都会弹出3,因为aa是一个对象,aa.i是对象的属性,只会分配一块内存。

 

不知道大家有木有理解了这个。

再来看一个问题吧,

var aa= [1,2,3]; varlength = aa.length;
for(var i = 0;i< length;i ++){
setTimeout(__,200);
 }

//实现依次弹出数值的值

 

for(var i = 0;i < len;i ++){
       setTimeout((function(i){
         return function(){
            alert(a[i]);
         }
        })(i),200);
    }

同理,也是利用了闭包和自执行函数。

 

再看一段代码:(简称为a代码)

 

for(var i = 0;i < 3;i ++){
         setTimeout(function(){
          alert(i);
         },0);
}

 

猜一下函数的输出结果,3,3,3,为什么!!!

这段代码和

for(var i = 0;i < 3;i ++){(简称为b代码)
alert(i);
}

并不一样!分析一下代码吧,b代码没什么好说的,执行for循环的时候,每次弹出i值,0,1,2,再看一下a代码,虽然用setTimeout,但是延时是0毫秒,就是没有延时啊,为什么i值成了3,因为js是单线程,js在执行第一次for循环的时候看到了setTimeout,要等0毫秒之后再去执行这个函数,于是来一个计时器,等0毫秒后再把这个函数加入等待队列,等待被执行,而这时候js还在执行for循环,在忙,所以这个函数就等着被执行,于是第二个第三个函数在满足执行条件的时候也被加入到等待队列。等for循环执行完才会去执行。

 

 

    

猜你喜欢

转载自blog.csdn.net/smallsun_229/article/details/51189279