类似jquery 的 .index();
就一级,类似
<ul id="myul">
<li>click</li>
<li>click</li>
<li>click</li>
<li>click</li>
<li>click</li>
</ul>
var nodeList = document.getElementsByTagName('li');
for(var i = 0;i<nodeList.length;i++){
//nodeList[i].index = i
nodeList[i].addEventListener("click", function(e){
console.log(nodeList.indexOf(nodeList[i]))
}, false);
}
我写的貌似有了闭包的问题,更正我的代码也好,我乱掉了。。。。
请问要如何实现呢?
比起“闭包”问题,我更倾向于它是一个作用域的问题。
不少前端都错用了“闭包”这个词,本文将尽量避免使用这个词。
看看你的代码:
ECMAScript规定,独立作用域只能通过“函数(function)”代码类型的执行上下文创建。
其他方式,for循环,
{}
代码块,定义函数,全部都不会产生新的作用域。上述代码中有没有新的作用域?有的,你用了一些形如
function(e){}
的匿名函数字面量,在执行时,每一个都会创建一个新的作用域。你的这些匿名函数字面量不会立即执行,它被设定为,在点击时才执行,这里它仅仅是被定义,被当做参数传入addEventListener函数而已。
在你的代码中,i和nodeList处于你的代码的顶级作用域之中,这个i在内存中只有一份。很显然,你的问题出在“为何让五个回调函数公用一个变量
i
”。——当你点击的时候,for循环早就已经执行完毕,调用会沿着作用域链,逐级往上找,直到找到那个已经用完到达5的i。
所以我们要解决这个问题,必须缓存i。
解法
1
以下方案在于,在于使用立即执行函数,通过函数的实参缓存i:
2
3
以下方案其实是使用函数调用缓存实参的变种,它将index缓存在forEach循环的函数调用中:
之前有个答案用到了数组,其实不需要再额外用立即执行函数。
下面是forEach的实现方式(并不完全,没有包含对边界状态的控制)。
我们可以看到,forEach循环在遍历过程中调用了
fn.call()
,通过函数调用,构建了新的函数上下文,缓存了函数当时的实参k。4
以下方案在于,把index值缓存在DOM属性中:
5
6
以下方案在于,把index值缓存在HTMLLIElement对象中(也包括Fakefish的答案),将变量查找转为属性查找:
7
以下方案在于利用ES5中的indexOf方法,只是需要预先将DOM元素转为Array:
8
LZ把几种方案混用了哦(包括在博客中),其实是没有必要的。
本想扔几个链接详细解释词法作用域的,结果发现大部分原文都消失了。前端就是这样,好文章混杂在一大堆烂解释中,还动不动就消失……所以说做好知识储备是非常重要的。
还好汤姆大叔的博客还在^^,这个系列真是百推不厌。好好读一读其中的第十一篇到第十六篇吧:)