从javascript的循环问题来看待闭包本质
第一次接触这个问题还是在我刚开始学js的时候,当时就是一头雾水,时隔一年多了,突然又想起了这个问题,在这个春气盎然的周末,我就坐下来研究下并把结果和大家分享下;
先看代码:demo.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8"/>
<title>闭包循环问题</title>
<style type="text/css">
p {background:red;}
</style>
</head>
<body>
<p id="p0">段落0</p>
<p id="p1">段落1</p>
<p id="p2">段落2</p>
<p id="p3">段落3</p>
<p id="p4">段落4</p>
<script type="text/javascript" src="jquery-1.7.js"></script>
<script type="text/javascript">
~function test() {
for( var i=0; i<5; i++ ) {
$("#p"+i).bind("click", function() {
alert(i);
});
};
}()
</script>
</body>
</html>
每次循环就为对应的编号段落上添加一个click事件,事件的回调函数是执行一个alert();如果你以前没这么用过的话,估计也会认为单击某个段落就会弹出这个段落相应的编号0,1,2,3,4。但实际上是都是弹出5;
网上已经有很多讨论的博客了,他们给出了很多方法去实现弹出对应的编号。比较易于理解的方法如下:
1,将变量i保存在对应的段落的某个属性上:
~function test() {
for( var i=0; i<5; i++ ) {
$("#p"+i).bind("click", function() {
alert($(this).attr("index"));
}).attr("index",i);
};
}();
2,加一层闭包,i 以函数参数形式传递给内层函数:
~function test() {
for( var i=0; i<5; i++ ) {
(function(param){
$("#p"+i).bind("click", function() {
alert(param);
});
})(i); };
}()
当然还有其他一些方法,但是都不太好理解。
而我要探索的是,为什么demo.html中的返回值始终是5。网上的说法是“变量i是以指针或者变量地址方式保存在函数中”,因为只有按照这样理解,才能解释。可是仅仅凭借一个结论怎么才能服众了?
谈到指针或者变量地址这个话题,在C语言中倒是家常便饭了,但是在js这么性感的语言中,除了对象的及其对象属性的引用之外很少用到。一个基本的数据类型居然和指针拉上关系了,这勾起了探索的欲望。
3,试试下面的代码
~function test() {
var temp =10;
for( var i=0; i<5; i++ ) {
$("#p"+i).bind("click",function() {
alert(temp);
});
};
temp=20;
}();
它的执行结果是每个段落的弹出都是20,而不是10。说明当我们在单击的那个时候,temp的值已经是20。这是个似乎不需要我来说明,很显然的结果,因为在我们单击之前,temp已经被赋值为20了。
4,再看看下面的代码,我们在temp被改变值之前有程序去触发单击事件,弹出的是10;
~function test() {
var temp =10;
for( var i=0; i<5; i++ ) {
$("#p"+i).bind("click",function() {
alert(temp);
});
if(i===1){
$("#p0").trigger("click");
};
};
temp=20;
}();
这说明我们在绑定$("#p0")的单击事件回调函数本来是要返回10的,当我再次手动去单击p0段落时,弹出20的原因是因为temp的值改变了。也就说明,每次弹出时,访问到的是temp此刻的值,而不是绑定时候的值;这可以说明在函数内部取得了变量的引用,而不是变量的值。扩展开去就是:函数内部访问了与函数同级的变量,那么该变量是常驻内存的。访问该变量实质上是访问的是变量的地址;
通过以上的结论,那么我们可以简单的描述闭包的本质了:在子作用域中保存了一份在父级作用域取得的变量,这些变量不会随父级作用域的销毁而销毁,因为他们已经常驻内存了!
这句话也就说明了闭包的特性了:1:因为常驻内存所以会造成内存泄露 2,只要其他作用域能取到子作用域的访问接口,那么其他作用域就有方法访问该子作用域父级作用域的变量了。
看这样一典型的闭包的例子:
function A(){
var a=1; function B(){
return a++;
}; return B;
}; var C=A();//C取得A的子作用域B的访问接口
console.log(C());//2 C能访问到B的父级作用域中的变量a
以上若有不足之处,欢迎指正,共同进步!
开心一刻:
从javascript的循环问题来看待闭包本质的更多相关文章
- 前端(十三)—— JavaScript高级:回调函数、闭包、循环绑定、面向对象、定时器
回调函数.闭包.循环绑定.面向对象.定时器 一.函数高级 1.函数回调 // 回调函数 function callback(data) {} // 逻辑函数 function func(callbac ...
- JavaScript: 零基础轻松学闭包
本文面向初学者,大神轻喷. 闭包是什么? 初学javascript的人,都会接触到一个东西叫做闭包,听起来感觉很高大上的.网上也有各种五花八门的解释,其实我个人感觉,没必要用太理论化的观念来看待闭包. ...
- javascript之循环保存数值
javascript之循环保存数值 语言都是相通的,这句话在我学javascript时有的深刻的意识.js中的for循环与java中的for循环有很大相似之处. 先看下面这段代码 for(var i= ...
- Jquery和Javascript 实际项目中写法基础-闭包 (2)
一.什么是闭包? 概念性的我就不去百度了,感兴趣的可以自己去搜下,我自己的理解,闭包就是一个封装的包,相当于类的概念,把乱七八糟的的东西封装到一起,然后统一使用一个对象来调用,实现代码部分对外开放,部 ...
- JavaScript的循环语句
JavaScript的循环语句 1.JavaScript的循环语句 (1)for循环语句 - 循环代码块一定的次数: (2)for/in循环语句 - 循环遍历对象的属性: (3)while循环语句 - ...
- JavaScript while 循环
JavaScript while 循环的目的是为了反复执行语句或代码块. 只要指定条件为 true,循环就可以一直执行代码块. while 循环 while 循环会在指定条件为真时循环执行代码块. 语 ...
- 【译】学习JavaScript中提升、作用域、闭包的终极指南
这似乎令人惊讶,但在我看来,理解JavaScript语言最重要和最基本的概念是理解执行上下文.通过正确学习它,你将很好地学习更多高级主题,如提升,作用域链和闭包.考虑到这一点,究竟什么是"执 ...
- 【前端开发】关于闭包最通俗易懂的解释 for循环,定时器,闭包混合一块的那点事。
for循环,定时器,闭包混合一块的那点事. 1,对于一个基本的for循环,顺序输出变量值. for(var i = 1; i < 4; i++){ console.log(i);//结果不多说了 ...
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
随机推荐
- IOS-内存分析
一.内存分析 1.静态分析(Analyze) 不运行程序, 直接检测代码中是否有潜在的内存问题(不一定百分百准确, 仅仅是提供建议) 结合实际情况来分析, 是否真的有内存问题 2.动态分析(Profi ...
- Redis 数据备份与恢复,安全,性能测试,客户端连接,管道技术,分区(四)
Redis 数据备份与恢复 Redis SAVE 命令用于创建当前数据库的备份. 语法 redis Save 命令基本语法如下: redis 127.0.0.1:6379> SAVE 实例 re ...
- Java基础摘要(一)
三大特性 封装 所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏.封装是面向对象的特征之一,是对象和类概念的主要特性.简单的说,一 ...
- shell与正则表达式
作业一:整理正则表达式博客 已整理完.作业二:grep作业(正则表达式及字符处理) 目标文件/etc/passwd,使用grep命令或egrep 1.显示出所有含有root的行:[root@bogon ...
- selenium2中关于Python的常用函数
driver = webdriver.Chrome(chromeDriver) 1.返回当前会话中的cookies:driver.get_cookies() 2.根据cookies name查找:dr ...
- 《修炼Java开发技术 在架构中体验设计模式和算法之美》 - 书摘精要
(P7) 建议直接加入到软件公司中去,这样会学到很多实际的东西: 程序员最主要的发展方向是资深技术专家,无论是 Java..Net 还是数据库领域,都要首先成为专家,然后才可能继续发展为架构师: 增强 ...
- Android中从SD卡中读取歌曲
先看看我的效果图吧 Activity类 private TextView nameTextView; private SeekBar seekBar; private ListView listVie ...
- ss-libev 源码解析local篇(1): ss_local的启动,客户端连入
学习研究ss-libev的一点记录(基于版本3.0.6) ss_local主要代码在local.c中,如果作为一个库编译,可通过start_ss_local_server启动local server. ...
- TCP/IP网络协议攻击
kali视频学习请看 http://www.cnblogs.com/lidong20179210/p/8909569.html 这部分涉及: ARP缓存欺骗攻击 ICMP重定向攻击 SYN FLOOD ...
- volley源码解析-Throwable类源码解析
前提知识点: 1.Serializable接口 作用:表示可序列化的语义.就是Java提供的通用数据保存和读取接口.任何类型实现了Serializeable接口,就可以被保存到文件中,或者作为数据流通 ...