这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
最近有很多朋友问我,为什么点击弹出的 i 总是最后一个,于是抽时间写了这篇文章,特此分享到 phperz 平台,供大家参考
在前端群里看见过很多人问过这个问题,今晚又有人问了这个问题,所以写篇文章整理一下。首先看一下代码, 点击 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 的函数
点击其中一项需要 alert 出如下结果:
按照我们平常的想法,代码应该是这样写的:
- 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();
但是不巧的是产生的结果是这样的:
索引 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>
(adsbygoogle = window.adsbygoogle || []).push({});
来源: http://www.phperz.com/article/17/0701/267742.html