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 相关文章推荐
商城常用滚动的焦点图效果代码简单实用
Mar 28 Javascript
Javascript 读取操作Sql中的Xml字段
Oct 09 Javascript
JavaScript 学习笔记之操作符(续)
Jan 14 Javascript
js获取当前日期前七天的方法
Feb 28 Javascript
javascript实现控制文字大中小显示
Apr 28 Javascript
javascript文本模板用法实例
Jul 31 Javascript
举例讲解jQuery中可见性过滤选择器的使用
Apr 18 Javascript
Bootstrap Table使用整理(四)之工具栏
Jun 09 Javascript
详解webpack进阶之loader篇
Aug 23 Javascript
React/Redux应用使用Async/Await的方法
Nov 16 Javascript
mpvue小程序仿qq左滑置顶删除组件
Aug 03 Javascript
Vue.js计算机属性computed和methods方法详解
Oct 12 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
PHP常用函数小技巧
2008/09/11 PHP
浅谈htmlentities 、htmlspecialchars、addslashes的使用方法
2016/12/09 PHP
PHP数据库操作二:memcache用法分析
2017/08/16 PHP
PHP实现Snowflake生成分布式唯一ID的方法示例
2020/08/30 PHP
javascript 写类方式之四
2009/07/05 Javascript
JS通过相同的name进行表格求和代码
2013/08/18 Javascript
html5+javascript制作简易画板附图
2014/04/25 Javascript
javascript实现阻止iOS APP中的链接打开Safari浏览器
2014/06/12 Javascript
JavaScript实现的GBK、UTF8字符串实际长度计算函数
2014/08/27 Javascript
Javascript实现快速排序(Quicksort)的算法详解
2015/09/06 Javascript
Angularjs中如何使用filterFilter函数过滤
2016/02/06 Javascript
jQuery获取剪贴板内容的方法
2016/06/16 Javascript
javascript稀疏数组(sparse array)和密集数组用法分析
2016/12/28 Javascript
JS跨域请求外部服务器的资源
2017/02/06 Javascript
JS实现复选框的全选和批量删除功能
2017/04/05 Javascript
详解如何让Express支持async/await
2017/10/09 Javascript
浅谈jquery fullpage 插件增加头部和版权的方法
2018/03/20 jQuery
微信小程序数据统计和错误统计的实现方法
2019/06/26 Javascript
js实现登录拖拽窗口
2020/02/10 Javascript
vant-ui组件调用Dialog弹窗异步关闭操作
2020/11/04 Javascript
javascript实现倒计时提示框
2021/03/02 Javascript
Python使用multiprocessing创建进程的方法
2015/06/04 Python
Python中MySQLdb和torndb模块对MySQL的断连问题处理
2015/11/09 Python
Python返回数组/List长度的实例
2018/06/23 Python
Python2和Python3.6环境解决共存问题
2018/11/09 Python
详解从Django Allauth中进行登录改造小结
2019/12/18 Python
如何定义TensorFlow输入节点
2020/01/23 Python
python 实现字符串下标的输出功能
2020/02/13 Python
萌新的HTML5 入门指南
2020/11/06 HTML / CSS
澳大利亚领先的女帽及配饰公司:Morgan&Taylor
2019/12/01 全球购物
毕业自我鉴定
2013/11/05 职场文书
给老师的一封感谢信
2015/01/20 职场文书
思想工作总结范文
2015/08/12 职场文书
师德培训心得体会2016
2016/01/09 职场文书
Python基础知识之变量的详解
2021/04/14 Python
pytorch交叉熵损失函数的weight参数的使用
2021/05/24 Python