最近遇到了一个问题,一个<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循环执行完才会去执行。