关于a的索引问题,这是我在“添加删除记录练习”中碰到的一个问题,在经过多次查阅资料和测试之后,发现了问题的答案(因为这是我本人的个人学习笔记,所以对于练习我都是直接复制粘贴自己写的代码,并没有过多的讲解编写代码的过程,而这次碰到了一个让我困扰许久的问题,在大量的测试和查阅资料之后,我发现了这其实是一个很简单的问题,单独写一章, 这是我学习js中碰到问题的一点感悟,希望碰到类似问题的初学者看到之后也能有所收获,如果是大神就当我没说。。。)
这是我碰到的问题
问题:http://t.csdn.cn/CQCQE?spm=1005.2026.3001.5622
这是我完整的代码
添加删除记录练习:http://t.csdn.cn/f9QkN
这是我简化后的问题模型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
window.onload = function(){
var alla = document.getElementsByTagName("a");
for(var i=0;i<alla.length;i++){
alla[i].onclick = function(){
//我通过不断的测试之后,发现alla[i]的类型是undefined
//而this的类型是object
//就是因为类型不一样,所以导致结果不一样
//使用this才能正常引用对象,而使用数组会报错
alert(typeof alla[i]);
};
}
};
</script>
</head>
<body>
<a href="">123</a>
<a href="">456</a>
<a href="">789</a>
</body>
</html>
为什么在事件函数里的alla[i]会是未定义?我们先来看一下代码的执行流程:
- 首先是onload事件,会在文档载入时触发,然后执行里面的代码块
- 获取了所有的超链接节点,并以数组的形式返回给了alla
- 执行for循环
- 执行for循环里的单击响应函数
- 然后执行函数里的alert语句
问题出现在第三步,执行for循环。
for循环会在页面加载完成之后立即执行,而响应函数会在超链接被点击的时候才执行,当响应函数执行时,for循环已经执行完毕,for循环执行结束的条件是i<alla.length,也就是说当i等于3时,for循环结束,当执行响应函数时,i等于3,但是此时alla数组里一共有三个元素,alla[0],alla[1],和alla[2],并没有alla[3],所以当检测alla[3]时,返回的结果是未定义。
那么为什么三个超链接都返回undefined?不应该是最后一个超链接才会返回undefined吗?
正如的前面说的,当响应函数执行时,for循环已经执行完毕,此时alert语句里的i就是3,,无论哪一个超链接的响应函数里的alert语句中的i都是3,所以都会返回未定义。
当然,我在测试代码的过程中还碰到了一个问题:
为什么把alert(alla[i]);语句直接放在for循环里,它可以正常运行,而把它放在事件函数里,它的i就被固定了,一直都是3?
这是我自己对于事件函数的理解,如果不对欢迎指正,看下面的伪代码:
/*--------------------等价转换----------------------------------*/
alla[i].onclick = functiion(){
alert(alla[i]);
};
//以上单击响应函数可以等价转换为下列的代码
var fun = functiion(){
alert(alla[i]);
};
alla[i].onclick = fun;
//注意是fun而不是fun()
//fun是调用名字为fun的函数对象,
//而fun()是将fun函数的返回值赋值给alla[i]的单击事件
/*------------------for循环里的执行流程------------------------*/
for(var i=0;i<alla.length;i++){
//第一次循环,此时i=0
var fun = functiion(){
alert(alla[0]);
};
alla[0].onclick = fun;
//此时第一个超链接绑定了函数fun,里面的函数体为alert(alla[0]);
//第二次循环,此时i=1
var fun = functiion(){
alert(alla[1]);
};
alla[1].onclick = fun;
//此时第二个超链接绑定了函数fun,里面的函数体变为alert(alla[1]);
//这里就是关键的地方,当for循环执行时,i自增,
//而fun里的函数体跟着也变成了alert(alla[1]);
/*就像是
var a,b=3;
b=4;
a=b;
//然后输出a,得到的结果为4,因为进行了第二次赋值
//二次赋值改变了b的值
*/
//此时的第二次for循环相当于重新给fun函数赋值
//第三次循环,此时i=2
var fun = functiion(){
alert(alla[2]);
};
alla[2].onclick = fun;
//此时第三个超链接绑定了函数fun,里面的函数体变为alert(alla[2]);
//第四次,i=3,此时i=alla.length,循环结束
//当单击事件触发时,无论是哪一个超链接,调用的都是fun
//fun再次被执行,此时的i=3
var fun = functiion(){
alert(alla[3]);
};
//此时已经超出了数组的原长度,值为未定义
}
也就是说在for循环里的单击事件不能直接使用数组的索引来引用对象,会导致长度溢出,而且最后的结果无论是哪一个单击事件都是同一个结果,那么该怎么解决呢?
使用this关键字引用对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
window.onload = function(){
var alla = document.getElementsByTagName("a");
for(var i=0;i<alla.length;i++){
alla[i].onclick = function(){
alert(this.innerHTML);
//此时this引用的是alla数组中的某个元素
//哪一个超链接被点击,引用的就是哪个超链接
//不用担心数组的溢出问题,也不用担心对象的指向错误问题
};
}
};
</script>
</head>
<body>
<a href="">123</a>
<a href="">456</a>
<a href="">789</a>
</body>
</html>
使用let关键字声明变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
window.onload = function(){
var alla = document.getElementsByTagName("a");
for(let i=0;i<alla.length;i++){
//使用let声明的变量无法重复声明
//也就是说用let声明的变量不能进行二次赋值操作
alla[i].onclick = function(){
alert(alla[i].innerHTML);
};
}
}
</script>
</head>
<body>
<a href="">123</a>
<a href="">456</a>
</body>
</html>
以上就是我在学习中碰到的问题,以及解决方案,希望能对一些和我一样的初学者有一定的帮助。