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 相关文章推荐
fromCharCode和charCodeAt 方法
Dec 27 Javascript
用jQuery中的ajax分页实现代码
Sep 20 Javascript
用javascript替换URL中的参数值示例代码
Jan 27 Javascript
纯CSS3代码实现滑动开关效果
Aug 19 Javascript
JavaScript实现向右伸出的多级网页菜单效果
Aug 25 Javascript
jquery.cookie.js用法实例详解
Dec 25 Javascript
JavaScript新增样式规则(推荐)
Jul 19 Javascript
jQuery EasyUI 组件加上“清除”功能实例详解
Apr 11 jQuery
ReactNative之键盘Keyboard的弹出与消失示例
Jul 11 Javascript
Vue组件之全局组件与局部组件的使用详解
Oct 09 Javascript
推荐15个最好用的JavaScript代码压缩工具
Feb 13 Javascript
layui动态绑定事件的方法
Sep 20 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
CodeIgniter针对数据库的连接、配置及使用方法
2016/03/03 PHP
PHP基于ORM方式操作MySQL数据库实例
2017/06/21 PHP
PHP实现的登录页面信息提示功能示例
2017/07/24 PHP
PHP实现将几张照片拼接到一起的合成图片功能【便于整体打印输出】
2017/11/14 PHP
使用PHPUnit进行单元测试并生成代码覆盖率报告的方法
2019/03/08 PHP
PHP读取Excel内的图片(phpspreadsheet和PHPExcel扩展库)
2019/11/19 PHP
JS创建自定义表格具体实现
2014/02/11 Javascript
通过JS判断联网类型和连接状态的实现代码
2015/04/01 Javascript
基于JavaScript实现一定时间后去执行一个函数
2015/12/14 Javascript
老生常谈onBlur事件与onfocus事件(js)
2016/07/09 Javascript
微信小程序五星评分效果实现代码
2017/04/06 Javascript
详解vue+vueRouter+webpack的简单实例
2017/06/17 Javascript
利用vue.js实现被选中状态的改变方法
2018/02/08 Javascript
vue 使用vue-i18n做全局中英文切换的方法
2018/10/29 Javascript
JS基于开关思想实现的数组去重功能【案例】
2019/02/18 Javascript
微信小程序实现的一键复制功能示例
2019/04/24 Javascript
VUE Elemen-ui之穿梭框使用方法详解
2021/01/19 Javascript
JS实现炫酷轮播图
2020/11/15 Javascript
Python装饰器decorator用法实例
2014/11/10 Python
Python数据库的连接实现方法与注意事项
2016/02/27 Python
python 实现删除文件或文件夹实例详解
2016/12/04 Python
python实现傅里叶级数展开的实现
2018/07/21 Python
详解python Todo清单实战
2018/11/01 Python
对python 中class与变量的使用方法详解
2019/06/26 Python
python threading和multiprocessing模块基本用法实例分析
2019/07/25 Python
Python 开发工具通过 agent 代理使用的方法
2020/09/27 Python
HTML5的语法变化介绍
2013/08/13 HTML / CSS
荷兰街头时尚之家:Funkie House
2019/03/18 全球购物
戴尔荷兰官方网站:Dell荷兰
2020/10/04 全球购物
生产部统计员岗位职责
2014/01/05 职场文书
乡下人家教学反思
2014/02/01 职场文书
模特职业生涯规划范文
2014/02/26 职场文书
设备收款委托书范本
2014/10/02 职场文书
党的群众路线教育实践活动个人整改措施落实情况
2014/11/04 职场文书
煤矿安全保证书
2015/02/27 职场文书
2019假期福利管理制度!
2019/07/15 职场文书