第一次接触这个问题还是在我刚开始学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的循环问题来看待闭包本质的更多相关文章

  1. 前端(十三)—— JavaScript高级:回调函数、闭包、循环绑定、面向对象、定时器

    回调函数.闭包.循环绑定.面向对象.定时器 一.函数高级 1.函数回调 // 回调函数 function callback(data) {} // 逻辑函数 function func(callbac ...

  2. JavaScript: 零基础轻松学闭包

    本文面向初学者,大神轻喷. 闭包是什么? 初学javascript的人,都会接触到一个东西叫做闭包,听起来感觉很高大上的.网上也有各种五花八门的解释,其实我个人感觉,没必要用太理论化的观念来看待闭包. ...

  3. javascript之循环保存数值

    javascript之循环保存数值 语言都是相通的,这句话在我学javascript时有的深刻的意识.js中的for循环与java中的for循环有很大相似之处. 先看下面这段代码 for(var i= ...

  4. Jquery和Javascript 实际项目中写法基础-闭包 (2)

    一.什么是闭包? 概念性的我就不去百度了,感兴趣的可以自己去搜下,我自己的理解,闭包就是一个封装的包,相当于类的概念,把乱七八糟的的东西封装到一起,然后统一使用一个对象来调用,实现代码部分对外开放,部 ...

  5. JavaScript的循环语句

    JavaScript的循环语句 1.JavaScript的循环语句 (1)for循环语句 - 循环代码块一定的次数: (2)for/in循环语句 - 循环遍历对象的属性: (3)while循环语句 - ...

  6. JavaScript while 循环

    JavaScript while 循环的目的是为了反复执行语句或代码块. 只要指定条件为 true,循环就可以一直执行代码块. while 循环 while 循环会在指定条件为真时循环执行代码块. 语 ...

  7. 【译】学习JavaScript中提升、作用域、闭包的终极指南

    这似乎令人惊讶,但在我看来,理解JavaScript语言最重要和最基本的概念是理解执行上下文.通过正确学习它,你将很好地学习更多高级主题,如提升,作用域链和闭包.考虑到这一点,究竟什么是"执 ...

  8. 【前端开发】关于闭包最通俗易懂的解释 for循环,定时器,闭包混合一块的那点事。

    for循环,定时器,闭包混合一块的那点事. 1,对于一个基本的for循环,顺序输出变量值. for(var i = 1; i < 4; i++){ console.log(i);//结果不多说了 ...

  9. c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类   public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...

随机推荐

  1. 011-对象——interface接口说明与使用方式实例

    <?php /** interface接口说明与使用方式实例 * * 接口里面的方法全是抽象方法,没有实体的方法.这样的类我们就叫做接口.定义的时候用Interface定义.实现接口时用impl ...

  2. 导出/打印项目数据报表需要设置IE浏览器

    导出/打印项目数据报表需要设置IE浏览器如下: 1.将本地服务器站点设置为可信站点, 2.通过点击网页上的工具→Internet选项→安全→自定义级别→把关于activeX控件和插件的选项都设置成启用 ...

  3. ng 通过factory方法来创建一个心跳服务

    <!DOCTYPE html> <html ng-app="myApp"> <head lang="en"> <met ...

  4. ng 变量和常量服务

    <!DOCTYPE html> <html ng-app="myApp"> <head lang="en"> <met ...

  5. go语言通过反射获取和设置结构体字段值的方法

    本文实例讲述了go语言通过反射获取和设置结构体字段值的方法.分享给大家供大家参考.具体实现方法如下: type MyStruct struct { N int } n := MyStruct{ 1 } ...

  6. DNS域名解析负载均衡

  7. Lua学习笔记(1)

    1.chunk是可以把Lua识别并执行的代码.chunk可以是单条语句,也可以是一系列语句的组合,还可以是函数,或是一个代码文件,交互模式下的每条指令(一行或多行)都是一个chunk交互模式下,判断指 ...

  8. C++之内存管理

    内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C++中无处不在,内存泄 ...

  9. LINUX TCP套接字详细配置

    提高服务器的负载能力,是一个永恒的话题.在一台服务器CPU和内存资源额定有限的情况下,最大的压榨服务器的性能,是最终的目的.要提高 Linux系统下的负载能力,可以先启用Apache的Worker模式 ...

  10. javascript的八张思维导图

    出处:http://www.cnblogs.com/junhey/p/4292683.html