JS常见问题之为什么点击弹出的i总是最后一个


Posted in Javascript onJanuary 05, 2016

在前端群里看见过很多人问过这个问题,今晚又有人问了这个问题,所以写篇文章整理一下。首先看一下代码,点击li之后弹出当前li所对应的索引值。于是很多人刷刷刷写出了下面的代码。

var aLi = document.getElementsByTagName('li');
for(var i = 0; i < aLi.length; i++){
  aLi[i].onclick = function(){
    alert(i);
  }
}

  但是结果不尽人意,为了简单,我们约定一下页面中有2个li。点击li之后弹出的都是2。
  我们首先来分析一下为什么结果是1.我们可以简单的将循环分成两部。

i = 0时,aLi[0].onclick = function(){alert(i)}
i = 1时,aLi[1].onclick = function(){alert(i)}
i = 2时,不满足条件跳出循环.

 在执行click的函数的时候,会有一个作用域链,这个作用域链是一个对象列表,这组对象定义了代码作用域中的变量。( 关于变量对象的内容想更详细了解的可以查看变量对象。)当我们alert(i)的时候,会去从内到外的去寻找变量i。这个时候这个函数的作用域链上有两个对象,这时循环已经结束了,i此时的值为2.所以点击任何一个li,弹出的都是2,而不是我们想要的索引值。重点在于弹出的是变量i,变量i,变量i。重要的事情说三遍。

 那么问题来了,我们要如何解决这个问题呢。我们需要做的就是在每次给aLi[i]绑定事件的时候,将这个时候i的值保存在内部的作用域中。解决方案如下。

var aLi = document.getElementsByTagName('li');
for (var i = 0; i < aLi.length; i++) {
 (function(i){
   aLi[i].onclick = function () {
      alert(i);
     };
 })(i)
}

这里涉及到一个块级作用域的概念。在es6出来前,函数是作为创建块级作用域的主要手段。这里我们通过在aLi[i].onclick外面套上一层函数,将i作为参数,我们重新分析一下结果。

i = 0时,
  (function(i){
   aLi[0].onclick = function(){
     alert(i);
   }
  })(0)
  i = 1时,
  (function(i){
   aLi[1].onclick = function(){
     alert(i);
   }
  })(1)
  i = 2时,不满足条件跳出循环.

由于多了一层自执行函数的包裹,当我们点击li时,会有三层的作用域,从内带外分别是:click函数内部的变量对象,自执行函数的变量对象和最外层的window对象。查找到第二层的时候,找到了i,自执行函数的i等于传入的参数值,相对应的存下了当时i的值,所以就弹出了相应的索引值。

下面再给大家分享一个js常见的问题,实现点击li能够弹出当前li索引与innerHTML的函数

JS常见问题之为什么点击弹出的i总是最后一个

点击其中一项需要alert出如下结果:

JS常见问题之为什么点击弹出的i总是最后一个

按照我们平常的想法,代码应该是这样写的:

var myul = document.getElementsByTagName("ul")[0];
  var list = myul.getElementsByTagName("li");
 
  function foo(){
    for(var i = 0, len = list.length; i < len; i++){
      list[i].onclick = function(){
        alert(i + "----" + this.innerHTML);
      }
    }
  }
  foo();

但是不巧的是产生的结果是这样的: 

JS常见问题之为什么点击弹出的i总是最后一个

索引index为什么总是4呢,这是js中没有块级作用域导致的。这里有三种解决思路

1. 使用闭包

<script type="text/javascript">
var myul = document.getElementsByTagName("ul")[0];
var list = myul.getElementsByTagName("li");
function foo(){
  for(var i = 0, len = list.length; i < len; i++){
    var that = list[i];
    list[i].onclick = (function(k){
      var info = that.innerHTML;
      return function(){
        alert(k + "----" + info);
      };
    })(i);
  }
}
foo();
</script>

2.使用ES6中的新特性let来声明变量

用let来声明的变量将具有块级作用域,很明显可以达到要求,不过需要注意的是得加个'use strict'(使用严格模式)才会生效

<script type="text/javascript">
var myul = document.getElementsByTagName("ul")[0];
var list = myul.getElementsByTagName("li");
function foo(){'use strict'
  for(let i = 0, len = list.length; i < len; i++){
    list[i].onclick = function(){
      alert(i + "----" + this.innerHTML);
    }
  }
}
foo();
</script>

3.引入jquery,使用其中的on或delegate进行事件绑定(它们都有事件代理的特性)

<script type="text/javascript" src="jquery-1.8.2.min.js"></script>

<script type="text/javascript">
$("ul").delegate("li", "click", function(){
  var index = $(this).index();
  var info = $(this).html();
  alert(index + "----" + info);
});
</script>
<script type="text/javascript">
$("ul").on("click", "li", function(){
  var index = $(this).index();
  var info = $(this).html();
  alert(index + "----" + info);
});
</script>
Javascript 相关文章推荐
JavaScript 学习技巧
Feb 17 Javascript
6个DIV 135或246间隔一秒轮番显示效果
Jul 24 Javascript
Jquery 类网页微信二维码图块滚动效果具体实现
Oct 14 Javascript
jQuery遍历对象、数组、集合实例
Nov 08 Javascript
Jquery+Ajax+PHP+MySQL实现分类列表管理(下)
Oct 28 Javascript
jQuery Form表单取值的方法
Jan 11 Javascript
了解VUE的render函数的使用
Jun 08 Javascript
VUE 使用中踩过的坑
Feb 08 Javascript
详解JavaScript中的强制类型转换
Apr 15 Javascript
如何测量vue应用运行时的性能
Jun 21 Javascript
vue excel上传预览和table内容下载到excel文件中
Dec 10 Javascript
Vue.js 实现地址管理页面思路详解(地址添加、编辑、删除和设置默认地址)
Dec 11 Javascript
浅谈javascript 函数表达式和函数声明的区别
Jan 05 #Javascript
JavaScript实现下拉菜单的显示和隐藏
Jan 05 #Javascript
jQuery实现二级下拉菜单效果
Jan 05 #Javascript
基于JavaScript实现简单的随机抽奖小程序
Jan 05 #Javascript
jquery中ajax处理跨域的三大方式
Jan 05 #Javascript
基于JavaScript代码实现随机漂浮图片广告
Jan 05 #Javascript
多个js毫秒倒计时同时进行效果
Jan 05 #Javascript
You might like
在IIS上安装PHP4.0正式版
2006/10/09 PHP
深入理解curl类,可用于模拟get,post和curl下载
2013/06/08 PHP
微信公众平台网页授权获取用户基本信息中授权回调域名设置的变动
2014/10/21 PHP
php读取远程gzip压缩网页的方法
2014/12/29 PHP
Zend Framework框架Smarty扩展实现方法
2016/03/22 PHP
eclipse php wamp配置教程
2016/06/30 PHP
php自定义函数实现JS的escape的方法示例
2016/07/07 PHP
php中curl和soap方式请求服务超时问题的解决
2018/06/11 PHP
js操作Xml(向服务器发送Xml,处理服务器返回的Xml)(IE下有效)
2009/01/30 Javascript
javascript重复绑定事件造成的后果说明
2013/03/02 Javascript
js判断IE浏览器版本过低示例代码
2013/11/22 Javascript
解决jquery中动态新增的元素节点无法触发事件问题的两种方法
2015/10/30 Javascript
总结JavaScript设计模式编程中的享元模式使用
2016/05/21 Javascript
总结Javascript中数组各种去重的方法
2016/10/04 Javascript
Vue.js基础学习之class与样式绑定
2017/03/20 Javascript
详解Vue.js 2.0 如何使用axios
2017/04/21 Javascript
Vue项目中quill-editor带样式编辑器的使用方法
2017/08/08 Javascript
Python的Twisted框架上手前所必须了解的异步编程思想
2016/05/25 Python
python中pandas.DataFrame排除特定行方法示例
2017/03/12 Python
Python字符串处理实例详解
2017/05/18 Python
django小技巧之html模板中调用对象属性或对象的方法
2018/11/30 Python
pandas读取csv文件提示不存在的解决方法及原因分析
2020/04/21 Python
python中什么是面向对象
2020/06/11 Python
利用Python优雅的登录校园网
2020/10/21 Python
如何利用Python写个坦克大战
2020/11/18 Python
纯CSS3实现表单验证效果(非常不错)
2017/01/18 HTML / CSS
英国的知名精品百货公司:House of Fraser(福来德)
2016/08/14 全球购物
联想马亚西亚官方网站:Lenovo Malaysia
2018/09/19 全球购物
Ibood荷兰:互联网每日最佳在线优惠
2019/02/28 全球购物
PHP数据运算类型都有哪些
2013/11/05 面试题
Solaris操作系统的线程机制
2015/07/28 面试题
构造方法和其他方法的区别
2016/04/26 面试题
教师通用专业自荐书范文
2014/02/11 职场文书
优秀中职教师事迹材料
2014/08/26 职场文书
征求意见函
2015/06/05 职场文书
vue3种table表格选项个数的控制方法
2022/04/14 Vue.js