javascript中异步和闭包产生的困惑
这里我不打算大谈特谈什么是异步,什么是闭包,这些内容在博客园都已经写的够多的了,但是这些内容出现的多,并不代表所有初学者都已经撑握了,所以我还是打算,用一个比较常见的示例来分析一下,或许能让对这个问题有困惑的同学有一种顿悟的感觉。我在上一篇博客《从一道面试题分析闭包>中已经分析过什么是闭包了,但是那个例子应用的场景比较复杂,不适合初学者理解,这里我举一个更常见的例子.
假如有这样一个需求:点击菜单中的每一项,显示所点击的内容,对应的内容页面如下:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head>
<body>
<ul>
<li>list 0</li>
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
<li>list 4</li>
</ul>
<script type="text/javascript"> </script>
</body>
</html>
我先来一段有问题的js代码,它是这样实现的:
var items = document.querySelectorAll('li');
var len = items.length;
for(var i =0;i<len;i++){
items[i].onclick = function(){
alert('list '+i)
}
}
这时候,我发现每一个li标签都邦定了点击事件,看起来运行的很好,可是当我多点几个,发现问题就来了,无论我点哪个li标签,弹出的都是list 5.这又是什么原因呢?
热心的网友马上给出回复说,因为弹出的i是循环之后的值。这样回答,那问题又来了:
1 :既然弹出的是循环之后的值,为什么每一个li标签上又都邦上了事件呢?
2 : 岂不是只有第5个li 元素可以邦上事件,而页面上根本不存这个元素,岂不是点击的时候,就该报错了呢?
说明这样回复,不但没有解释清楚问题,反而增加了更多的疑问,难道不是吗?这里产生疑问的根本原因在于有一个异步过程。循环代码是同步的,而点击操作是异步的。
我用一个游戏来演示这个过程:
操场上站着4个小朋友(对应4个 li元素),老师挨个的告诉他们游戏规则(对应循环),规则是这样的:呆会老师会举一个小黑板,然后点你名字的时候,你就告诉老师黑板上写的是什么字。(对应onclick所设定的function)。游戏开始了,老师在黑板上写了1,觉得不好,改为2,接着又改成3,最后改成4.小朋友们看着老师改来改去,等了好久才开始游戏。
可是无论点哪个小朋友,他回答的都是“4”。因为他们看到的都是老师最后写的那个数字。
现在我们再回过来看刚才的代码。
var items = document.querySelectorAll('li');
var len = items.length;
for(var i =0;i<len;i++){
//当i=0的时候,即items[0]所对应的元素,这是确定的。
//items[i]这里执行的是一个取值操作
items[i].onclick = function(){
//而这里边的i却要等到这个函数运行时才能确定是多少
//函数什么时候运行,肯定发生在循环之后了。因为点击的速度显然是比不过cpu运算的速度的
alert('list '+i)
}
}
刚才那个游戏,显然不是老师期望的。这次她把小黑板换成写好字的小纸片,挨个讲规则的时候顺便把纸片传给他们每一个人。这样每个小朋友手里都拿了一个属于自己的数字。老师点名字的时候,他们都报出了自己纸片上的数字。老师为自己的创意感到满意。
那我们这个程序,要怎么把i做成小纸片事先传给每一个li元素呢?
方法一:
在li上做一个标记,点的时候取这个标记上的值。
var items = document.querySelectorAll('li');
var len = items.length;
for(var i =0;i<len;i++){
items[i].setAttribute('i',i);
items[i].onclick = function(){
i = this.getAttribute("i");
alert('list '+i)
}
}
方法二:
利用闭包的特性
var items = document.querySelectorAll('li');
var len = items.length;
for(var i =0;i<len;i++){
items[i].onclick = (function(){
var t = i;
return function(){
alert('list '+t)
}
})()
}
方法三:利用闭包比较难理解,我们换一个方式
var items = document.querySelectorAll('li');
var len = items.length;
for(var i =0;i<len;i++){
items[i].onclick = function(t){
alert('list '+t)
}.bind(this,i)
}
总结一下:
以上虽然用了三种形式的小纸片进行传参,但是目的都是为了保证在循环之后,每个li的回调函数上的参数都能确定下来。这种小纸片在解决异步问题上,是一个有用的技巧。
关于异步的问题,还有很多,由于时间关系,就不再一一列举了,有兴趣的同学可以@我,一起学习。
如果您觉得这文章对您有帮助,请点击【推荐一下】,想跟我一起学习吗?那就【关注】我吧!
javascript中异步和闭包产生的困惑的更多相关文章
- JavaScript中异步编程
一 关于事件的异步 事件是JavaScript中最重要的一个特征,nodejs就是利用js这一异步而设计出来的.所以这里讲一下事件机制. 在一个js文件中,如果要运行某一个函数,有2中手段,一个就是直 ...
- 对JavaScript中异步同步机制以及线程深入了解
今天在网上看到各种对Js异步同步单线程多线程的讨论 经过前辈们的洗礼 加上鄙人小小的理解 就来纸上谈兵一下吧~ Js本身就是单线程的 至于为什么Js是单线程的 那就要追溯到Js的历史了 总而言之 由于 ...
- javascript中重要概念-闭包-深入理解
在上次的分享中javascript--函数参数与闭包--详解,对闭包的解释不够深入.本人经过一段时间的学习,对闭包的概念又有了新的理解.于是便把学习的过程整理成文章,一是为了加深自己闭包的理解,二是给 ...
- javascript中函数的闭包自调用
话不多说, 直接上代码 // 定义一个变量outerParam, 然后使用一个闭包函数给该变量初始化var outerParam = (function testClosure(param) { // ...
- 在Javascript中闭包(Closure)
在Javascript中闭包(Closure) 什么是闭包 “官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. ...
- Javascript中call,apply,bind方法的详解与总结
在 javascript之 this 关键字详解 文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变性.当在全局作用域时,thi ...
- JavaScript中的异步操作
什么是异步操作? 异步模式并不难理解,比如任务A.B.C,执行A之后执行B,但是B是一个耗时的工作,所以,把B放在任务队列中,去执行C,然后B的一些I/O等返回结果之后,再去执行B,这就是异步操作. ...
- 深入浅出:了解JavaScript中的call,apply,bind的差别
在 javascript之 this 关键字详解文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变 ...
- [ Javascript ] JavaScript中的定时器(Timer) 是怎样工作的!
作为入门者来说.了解JavaScript中timer的工作方式是非常重要的.通常它们的表现行为并非那么地直观,而这是由于它们都处在一个单一线程中.让我们先来看一看三个用来创建以及操作timer的函数. ...
随机推荐
- spring动态代理
接下来我们来体会下动态代理带给我们的便利 package aop006; public interface Girl { public void KFC(String datetime); publi ...
- arcmap插件不能调试(转)
1, 进入ArcGIS Desktop安装目录,如:C:\Program Files\ArcGIS\Desktop10.0 2, 进入Bin文件夹,备份ArcMap.exe.config文件,以备恢复 ...
- EXT 下拉框事件
1. <ext:ComboBox ID="cbline" FieldLabel="平台部门来源" runat="server" Dis ...
- EditPlus快捷键
Editplus 选中一行: ctrl+rEditplus 复制一行: ctrl+r选择行,然后ctrl+c复制.复制一行到下一行中:Editplus有:Ctrl+j 复制上一行的一个字符到当前行Ed ...
- Python swapcase()方法
首先,要明白Python swapcase() 方法用于对字符串的大小写字母进行转换. 其次,了解swapcase()方法语法:str.swapcase() 返回值:返回大小写字母转换后生成的新字符串 ...
- 【转】WriteMessage的信息在AutoCAD中命令行中实时显示
之前程序中有段发送信息到命令行上显示的代码,如下: ed.WriteMessage("开始标注横断面高程,请稍候!"); 但是发现命令行中并不马上显示,代码也明明运 ...
- 关于大型网站技术演进的思考(十九)--网站静态化处理—web前端优化—上(11)
网站静态化处理这个系列马上就要结束了,今天我要讲讲本系列最后一个重要的主题web前端优化.在开始谈论本主题之前,我想问大家一个问题,网站静态化处理技术到底是应该归属于web服务端的技术范畴还是应该归属 ...
- ystep jQuery流程、步骤插件
今天小菜给大家带来又一款给力jQuery插件:ystep. 从名称上大致可以看出,这是一个流程步骤插件. 如果无意外的话,这可能是小菜近期最后一个作品了...苦逼的小菜即将创业,可能就没时间折腾啦~好 ...
- JavaScript面试时候的坑洼沟洄——逗号、冒号与括号
看完了javaScript数据类型和表达式与运算符相关知识后以为可以对JavaScript笔试题牛刀小试一把了,没想到有一次次的死在逗号,冒号和括号上,不得已再看看这几个符号吧. 逗号 逗号我们常见的 ...
- Ubuntu系统字体安装
用惯了Windows,刚转到Ubuntu时总感觉字体显示没那么亲切,尤其是中文字体,在网页上显示特别怪.有些软件对中文字体的支持也不好,WebStorm中的Git logs中文也显示乱码.把系统语言设 ...