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 相关文章推荐
Extjs中ComboBox加载并赋初值的实现方法
Mar 22 Javascript
浅析js中2个等号与3个等号的区别
Aug 06 Javascript
JS实现日期加减的方法
Nov 29 Javascript
jQuery中each()方法用法实例
Dec 27 Javascript
JavaScript中的splice()方法使用详解
Jun 09 Javascript
微信小程序 获取相册照片实例详解
Nov 16 Javascript
JavaScript实现简单精致的图片左右无缝滚动效果
Mar 16 Javascript
vue2项目使用sass的示例代码
Jun 28 Javascript
JavaScript 保护变量不被随意修改的实现代码
Sep 27 Javascript
详解Angular cli配置过程记录
Nov 07 Javascript
mapboxgl区划标签避让不遮盖实现的代码详解
Jul 01 Javascript
Vue Mint UI mt-swipe的使用方式
Jun 05 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
PHP语法速查表
2007/01/02 PHP
认识并使用PHP超级全局变量
2010/01/26 PHP
php之CodeIgniter学习笔记
2013/06/17 PHP
ThinkPHP中的create方法与自动令牌验证实例教程
2014/08/22 PHP
php保存二进制原始数据为图片的程序代码
2014/10/14 PHP
curl和libcurl的区别简介
2015/07/01 PHP
php实现源代码加密的方法
2015/07/11 PHP
PHP实现笛卡尔积算法的实例讲解
2019/12/22 PHP
phpwind放自动注册方法
2006/12/02 Javascript
读jQuery之十二 删除事件核心方法
2011/07/31 Javascript
基于javascript 闭包基础分享
2013/07/10 Javascript
JavaScript 和 Java 的区别浅析
2013/07/31 Javascript
jQuery过滤选择器用法分析
2015/02/10 Javascript
Js调用Java方法并互相传参的简单实例
2016/08/11 Javascript
JavaScript trim 实现去除字符串首尾指定字符的简单方法
2016/12/27 Javascript
vue.js中mint-ui框架的使用方法
2017/05/12 Javascript
jquery动态添加以及遍历option并获取特定样式名称的option方法
2018/01/29 jQuery
vue.js项目nginx部署教程
2018/04/05 Javascript
一文读懂ES7中的javascript修饰器
2019/05/06 Javascript
layui form表单提交后实现自动刷新
2019/10/25 Javascript
javscript 数组扁平化的实现
2020/02/03 Javascript
JavaScript实现烟花绽放动画效果
2020/08/04 Javascript
Python中的测试模块unittest和doctest的使用教程
2015/04/14 Python
Golang与python线程详解及简单实例
2017/04/27 Python
python机器学习之神经网络(三)
2017/12/20 Python
详解pyenv下使用python matplotlib模块的问题解决
2018/11/29 Python
python3实现钉钉消息推送的方法示例
2019/03/14 Python
python在linux环境下安装skimage的示例代码
2020/10/14 Python
CSS3简单实现照片墙
2014/12/12 HTML / CSS
HTML5移动端开发中的Viewport标签及相关CSS用法解析
2016/04/15 HTML / CSS
Marriott中国:万豪国际酒店查询预订
2016/09/02 全球购物
Nisbets爱尔兰:英国最大的厨房和餐饮设备供应商
2019/01/26 全球购物
标记环介质访问控制协议
2016/03/27 面试题
酒店管理专业学生求职信
2013/09/27 职场文书
新闻专业应届生求职信
2013/10/31 职场文书
2015公司年度工作总结
2015/05/14 职场文书