博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
彻底弄懂js循环中的闭包问题
阅读量:4618 次
发布时间:2019-06-09

本文共 2703 字,大约阅读时间需要 9 分钟。

   第一次接触这个问题还是在我刚开始学js的时候,当时就是一头雾水,时隔一年多了,突然又想起了这个问题,在这个春气盎然的周末,我就坐下来研究下并把结果和大家分享下;

先看代码:

<!DOCTYPE HTML>
 <html>
  <head>
   <meta charset="gbk"/>
   <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"> 
 for( var i=0; i<5; i++ ) { 
   document.getElementById("p"+i).οnclick=function() { 
     alert(i); //访问了父函数的变量i, 闭包 
   };
 };
</script> 
</body> 
</html>
  每次循环就为对应的编号段落上添加一个click事件,事件的回调函数是执行一个alert();如果你以前没这么用过的话,估计也会认为单击某个段落就会弹出这个段落相应的编号0,1,2,3,4。但实际上是都是弹出5;

网上已经有很多讨论的博客了,他们给出了很多方法去实现弹出对应的编号。比较易于理解的方法如下:

1,将变量i保存在对应的段落的某个属性上

var pAry = document.getElementsByTagName("p");      for( var i=0; i< 5; i++ ) {       pAry[i].no = i;       pAry[i].onclick = function() {         alert(this.no);      }    };

2,加一层闭包,i 以函数参数形式传递给内层函数

~function test() {         var pAry = document.getElementsByTagName("p");         for( var i=0; i< pAry.length; i++ ) {          (function(arg){               pAry[i].onclick = function() {                  alert(arg);           };          })(i);//调用时参数        }     }();

当然还有其他一些方法,但是都不太好理解。

    而我要探索的是,为什么demo.html中的返回值始终是5。网上的说法是“变量i是以指针或者变量地址方式保存在函数中”,因为只有按照这样理解,才能解释。可是仅仅凭借一个结论怎么才能服众了?

   谈到指针或者变量地址这个话题,在C语言中倒是家常便饭了,但是在js这么性感的语言中,除了对象的及其对象属性的引用之外很少用到。一个基本的数据类型居然和指针拉上关系了,这勾起了探索的欲望。

3,试试下面的代码 

~function test() {           var temp =10;         for( var i=0; i< 5; i++ ) {           document.getElementById("p"+i).οnclick=function() {             alert(temp); //访问了父函数的变量temp, 闭包           }       };       temp=20;   }();

  它的执行结果是每个段落的弹出都是20,而不是10。说明当我们在单击的那个时候,temp的值已经是20。这是个似乎不需要我来说明,很显然的结果,因为在我们单击之前,temp已经被赋值为20了。

4,再看看下面的代码  
我们在temp被改变值之前有程序去触发单击事件,弹出的是10;

~function test() {           var temp =10;         for( var i=0; i< 5; i++ ) {           document.getElementById("p"+i).οnclick=function() {             alert(temp); //访问了父函数的变量i, 闭包           }        if(i===1){           var evt = document.createEvent("MouseEvents");           evt.initEvent("click",true,true);           document.getElementById("p1").dispatchEvent(evt);        }       };       temp=20;   }();

  这说明我们在p1上绑定的单击事件回调函数本来是要返回10的,当我再次手动去单击p0段落时,弹出20的原因是因为temp的值改变了。也就说明,每次弹出时,访问到的是temp此刻的值,而不是绑定时候的值;这可以说明变量temp确实是以变量地址保存的。扩展开去就是:函数内部访问了与函数同级的变量,那么该变量是常驻内存的。访问该变量实质上是访问的是变量的地址;

   通过以上的结论,那么我们可以简单的描述闭包的本质了:在子作用域中保存了一份在父级作用域取得的变量,这些变量不会随父级作用域的销毁而销毁,因为他们已经常驻内存了!

这句话也就说明了闭包的特性了:

1:因为常驻内存所以会造成内存泄露
2,只要其他作用域能取到子作用域的访问接口,那么其他作用域就有方法访问该子作用域父级作用域的变量了。

看这样一典型的闭包的例子:

  function A(){
   var a=1;
   function B(){
    return a;
  }; 
  return B;
 };
 var C=A();//C取得A的子作用域B的访问接口
 console.log(C());//1 C能访问到B的父级作用域中的变量a 
以上若有不足之处,欢迎指正,共同进步!

转载于:https://www.cnblogs.com/hljmly/p/5448134.html

你可能感兴趣的文章
BZOJ3675 [Apio2014]序列分割 动态规划 斜率优化
查看>>
2016.10.24 继续学习
查看>>
产品功能对标 - 服务授权管理
查看>>
各地IT薪资待遇讨论
查看>>
splay入门
查看>>
带CookieContainer进行post
查看>>
C语言学习笔记--字符串
查看>>
关于七牛进行图片添加文字水印操作小计
查看>>
DataSource数据库的使用
查看>>
Luogu4069 SDOI2016 游戏 树链剖分、李超线段树
查看>>
Java的内部类真的那么难以理解?
查看>>
一文搞懂Java环境,轻松实现Hello World!
查看>>
hash实现锚点平滑滚动定位
查看>>
也谈智能手机游戏开发中的分辨率自适应问题
查看>>
关于 IOS 发布的点点滴滴记录(一)
查看>>
《EMCAScript6入门》读书笔记——14.Promise对象
查看>>
CSS——水平/垂直居中
查看>>
Eclipse连接mysql数据库jdbc下载(图文)
查看>>
Python中Selenium的使用方法
查看>>
三月23日测试Fiddler
查看>>