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 16 Javascript
基于jQuery实现Ajax验证用户名是否存在实例
Mar 30 Javascript
一种Javascript解释ajax返回的json的好方法(推荐)
Jun 02 Javascript
JavaScript禁止用户多次提交的两种方法
Jul 24 Javascript
jQuery设置和获取select、checkbox、radio的选中值方法
Jan 01 Javascript
EasyUi 打开对话框后控件赋值及赋值后不显示的问题解决办法
Jan 19 Javascript
react-router4 嵌套路由的使用方法
Jul 24 Javascript
React Native实现地址挑选器功能
Oct 24 Javascript
ES6知识点整理之函数数组参数的默认值及其解构应用示例
Apr 17 Javascript
webpack是如何实现模块化加载的方法
Nov 06 Javascript
通过实例解析js可枚举属性与不可枚举属性
Dec 02 Javascript
vue 实现图片懒加载功能
Dec 31 Vue.js
浅谈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
laravel 验证错误信息到 blade模板的方法
2019/09/29 PHP
php使用event扩展的io复用测试的示例
2020/10/20 PHP
Jquery方式获取iframe页面中的 Dom元素
2014/05/07 Javascript
判断输入的字符串是否是日期格式的简单方法
2016/07/11 Javascript
jQuery实现的省市县三级联动菜单效果完整实例
2016/08/01 Javascript
JS打印组合功能
2016/08/04 Javascript
javaScript 事件绑定、事件冒泡、事件捕获和事件执行顺序整理总结
2016/10/10 Javascript
jquery实现input框获取焦点的简单实例
2017/01/26 Javascript
微信小程序-获得用户输入内容
2017/02/13 Javascript
React 使用browserHistory项目访问404问题解决
2018/06/01 Javascript
解决vuejs 使用value in list 循环遍历数组出现警告的问题
2018/09/26 Javascript
JS实现“全选”和&quot;全不选&quot;功能代码实例
2020/02/06 Javascript
python使用rsa加密算法模块模拟新浪微博登录
2014/01/22 Python
值得收藏,Python 开发中的高级技巧
2018/11/23 Python
Python 通过微信控制实现app定位发送到个人服务器再转发微信服务器接收位置信息
2019/08/05 Python
Python warning警告出现的原因及忽略方法
2020/01/31 Python
解决Pycharm 导入其他文件夹源码的2种方法
2020/02/12 Python
appium+python adb常用命令分享
2020/03/06 Python
详解Python之Scrapy爬虫教程NBA球员数据存放到Mysql数据库
2021/01/24 Python
精致的手工皮鞋:Shoe Embassy
2019/11/08 全球购物
JBL美国官方商店:扬声器、耳机等
2019/12/01 全球购物
Yahoo-PHP面试题1
2016/07/20 面试题
介绍一下RMI的基本概念
2016/12/17 面试题
儿科护理实习自我鉴定
2013/09/19 职场文书
大学生实习思想汇报
2014/01/12 职场文书
学术会议主持词
2014/03/17 职场文书
计算机专业自荐信
2014/05/24 职场文书
大学生推广普通话演讲稿
2014/09/21 职场文书
中学生检讨书范文
2014/11/03 职场文书
2014年食品安全工作总结
2014/12/04 职场文书
2014年纪委工作总结
2014/12/05 职场文书
2015教师个人工作总结范文
2015/03/31 职场文书
2015年社区流动人口工作总结
2015/05/12 职场文书
Redis遍历所有key的两个命令(KEYS 和 SCAN)
2021/04/12 Redis
Python编程根据字典列表相同键的值进行合并
2021/10/05 Python
Android学习之BottomSheetDialog组件的使用
2022/06/21 Java/Android