我主要是把鼠标点击li元素,弹出各自索引值的几种方法进行总结。
问题:如下代码所示有好多li元素。实现的效果是当鼠标点击某个li元素时弹出自己的索引值(当然这里不是弹出它的innerHTML,这是凑巧索引值和它的innerHTML相等)
<ul id="ul1">
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
12345678我们通常的做法是:
var aLi=document.getElementById('ul1')
.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++)
{
aLi[i].onclick=function(){
alert(i);
}
}
12345678这是否如我们所愿呢,经过测试我们会发现,这段代码不管我们点击哪个都会弹出的是6.
这是为什么?
首先解释6是怎么来的,我们可以知道,这个循环的终止条件是i<aLi.length。也就是i不再<6。条件首次成立i 的值是6。因此,输出显示的是循环结束时i的最终值。
这里引入一个更深入的问题,代码中到底有什么缺陷导致它的行为同语义所暗示的不一致呢?
缺陷是我们试图假设循环中的每个迭代在运行时都会给自己“捕获”一个i的副本,但是根据作用域的工作原理,实际情况是尽管循环中的6个函数是在各个迭代中分别定义的,但是他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
这样说的话,当然所有函数共享一个i的引用
我们可以通过声明并立即执行函数表达式来创建作用域。
var aLi=document.getElementById('ul1')
.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++)
{
(function(){
var j=i;
aLi[j].onclick=function(){
alert(j);
}
})();
}
123456789101112当然我们也可以对这段代码进行改进
var aLi=document.getElementById('ul1')
.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++)
{
(function(j){
aLi[j].onclick=function(){
alert(j);
}
})(i);
}
1234567891011当然,这些IIFE也不过就是函数,因此我们可以将i传递进去,至于参数的话名字随便起都行。
在迭代内使用IIFE会为每一个迭代都生成一个新的作用域,使得点击函数可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。
考虑我们对前面的问题的解决方案的分析。我们是在每次迭代时都创建了一个新的作用域,换句话说,每次迭代我们都需要一个块作用域。我们可以使用let劫持块作用域。
var aLi=document.getElementById('ul1')
.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++)
{
let j=i;//闭包的块作用域
aLi[j].onclick=function(){
alert(j);
}
}
123456789我们可以进行代码优化
var aLi=document.getElementById('ul1')
.getElementsByTagName('li');
for(let i=0;i<aLi.length;i++)
{
aLi[i].onclick=function(){
alert(i);
}
}
12345678for循环头部的let声明还会有一个特殊的行为。这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明。随后的每个迭代都会用上一个迭代结束时的值来初始化这个变量。
很酷是吧?块作用域和闭包联手,天下无敌。
最后我们再说一个方法
var aLi=document.getElementById('ul1').
getElementsByTagName('li');
for(var i=0;i<aLi.length;i++)
{
aLi[i].index=i;
aLi[i].onclick=function(){
alert(this.index);
}
}
还没有评论,来说两句吧...