下面的代码中,为什么elems[i]的结果是undefined?

下面的代码中,为什么elems[i]得到的结果是:undefined?

代码及运行效果也可在JS Bin中查看:

代码及运行结果

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JS Bin</title>
</head>
<body>
  <div class="link">
    <a href="#">壹</a>
    <a href="#">贰</a>
    <a href="#">叁</a>
    <a href="#">肆</a>
  </div>
</body>
</html>
var elems = document.getElementsByTagName("a");

for(var i = 0; i < elems.length; i++ ){
    elems[i].addEventListener("click",function(){
      console.log("i = " + i);
    //运行结果:"I am link # undefined",为什么得到的elems[i]是undefined呢?   
      console.log("I am link # " + elems[i]); 
  });
    
}
阅读 2.7k
4 个回答

首先

for语句中使用var声明变量是的作用域问题,在MDN中是这样描述的:该表达式可以使用var或let关键字声明新的变量,使用var声明的变量不是该循环的局部变量,而是与for循环处在同样的作用域中。用let声明的变量是语句的局部变量
因此你使用var i=0;是相当于声明了一个跟for语句同级的变量i;等价的写法类似:

var i;
for(i=0;.......){
    ........
}

然后

理解了上面的话,下面理解值为undefined就不难了,此时我们再看循环:我们在DOM中有四个元素,因此elems.length的值为4;由于for循环值是我们对每个DOM绑定了一个点击函数,函数的触发执行的时机肯定是晚于for选执行的时间的。因此,每个点击事件最终拿到的i都是for循环满足条件后的i(注意 i++ ),也就是4;这时需要注意数组访问是以0开始的,因此DOM元素的每个下标是 0 1 2 3 这时你去访问elems[4]就是在访问一个数组中不存在的值,肯定是undefined

解决办法可以采用let ;let具有块级作用域可以解决上面的问题,或者使用自执行函数闭包解决;
更详细的内容可以看:https://segmentfault.com/a/11...

你没发现你的i都是4吗, elems[4]自然是undefined
var改成let

因为for循环后, i的值为4, 而elems长度为4, elems[4] 为 undefined

把i使用let声明

var elems = document.getElementsByTagName("a");

for(let i = 0; i < elems.length; i++ ){
    elems[i].addEventListener("click",function(){
      console.log("i = " + i);
      console.log("I am link # " + elems[i]); 
  });
}

或者使用forEach, 产生函数作用域来暂存那一趟循环i的值

var elems = document.getElementsByTagName("a");

Array.prototype.forEach.call(elems, function(el, i) {
    elems[i].addEventListener("click",function(){
      console.log("i = " + i);
      console.log("I am link # " + elems[i]); 
  });
})

在最后一次循环中 i 还执行了一次 i++,此时 i 为 4 ,执行完后不满足 i < elems.length 的循环条件,退出循环。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题