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 相关文章推荐
解密效果
Jun 23 Javascript
javascript编程起步(第二课)
Feb 27 Javascript
jQuery 使用手册(二)
Sep 23 Javascript
JSON 数字排序多字段排序介绍
Sep 18 Javascript
jQuery模拟黑客帝国矩阵效果实例
Jun 28 Javascript
详解JavaScript对象和数组
Dec 03 Javascript
Node.js中npm常用命令大全
Jun 09 Javascript
浅谈js中test()函数在正则中的使用
Aug 19 Javascript
Vue生命周期示例详解
Apr 12 Javascript
web前端vue之vuex单独一文件使用方式实例详解
Jan 11 Javascript
vue 中动态绑定class 和 style的方法代码详解
Jun 01 Javascript
jQuery实现带进度条的轮播图
Sep 13 jQuery
浅谈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
手把手教你使用DedeCms V3的在线采集图文教程
2007/04/03 PHP
windows下配置apache+php+mysql时出现问题的处理方法
2014/06/20 PHP
PHP中require和include路径问题详解
2014/12/25 PHP
JSON两种结构之对象和数组的理解
2016/07/19 PHP
自写的利用PDO对mysql数据库增删改查操作类
2018/02/19 PHP
PHP实现的获取文件mimes类型工具类示例
2018/04/08 PHP
PHP生成指定范围内的N个不重复的随机数
2019/03/18 PHP
认识延迟时间为0的setTimeout
2008/05/16 Javascript
jQuery load方法用法集锦
2011/12/06 Javascript
javascript 使td内容不换行不撑开
2012/11/29 Javascript
Dom 学习总结以及实例的使用介绍
2013/04/24 Javascript
jQuery的缓存机制浅析
2014/06/07 Javascript
JavaScript通过字典进行字符串翻译转换的方法
2015/03/19 Javascript
使用JavaScript 实现的人脸检测
2015/03/24 Javascript
浅谈js中的延迟执行和定时执行
2016/05/31 Javascript
jQuery中的on与bind绑定事件区别实例详解
2017/02/28 Javascript
基于百度地图api清除指定覆盖物(Overlay)的方法
2018/01/26 Javascript
浅谈Vue的响应式原理
2019/05/30 Javascript
Angular中innerHTML标签的样式不起作用的原因解析
2019/06/18 Javascript
Nodejs libuv运行原理详解
2019/08/21 NodeJs
深入理解Antd-Select组件的用法
2020/02/25 Javascript
python 判断自定义对象类型
2009/03/21 Python
python抓取京东商城手机列表url实例代码
2013/12/18 Python
Python自然语言处理之词干,词形与最大匹配算法代码详解
2017/11/16 Python
在Python中使用Neo4j的方法
2019/03/14 Python
python tools实现视频的每一帧提取并保存
2020/03/20 Python
Django 后台获取文件列表 InMemoryUploadedFile的例子
2019/08/07 Python
Python3 pandas 操作列表实例详解
2019/09/23 Python
Python使用Pyqt5实现简易浏览器(最新版本测试过)
2020/04/27 Python
零基础学python应该从哪里入手
2020/08/11 Python
美国礼品卡商城: Gift Card Mall
2017/08/25 全球购物
国际象棋商店:The Chess Store
2018/07/09 全球购物
亚洲最大的眼镜批发商和零售商之一:Glasseslit
2018/10/08 全球购物
党风廉设责任书
2014/04/16 职场文书
教导主任竞聘演讲稿
2014/05/16 职场文书
使用CSS设置滚动条样式
2022/01/18 HTML / CSS