js闭包理解

//做一只蜗牛吧,一步一步往上爬;但傻逼才愿意,蜗牛速度那么慢

//转行做个新手,发现写代码博客啥的真需要一股认真劲儿,在这里贡献一份js函数(闭包)学习心得,不当之处望各路高手们指点,谢过。

函数的结构

先上图。图中表示一个普通函数,去解构它可以看到:申明函数的关键字function,函数名f,形参x——>继续到函数里面来看到:申明变量的关键字var,变量名x,y,内部函数g(y),以及函数体代码。


才开始学函数的时候,就把函数的几种定义形式(函数直接量,申明函数,new实例化构造函数)过了一遍,好像还不难理解哈,但随着学习深入,发现在js里用函数处处出问题——你知道这是新手、很新的那种的原因,后来发现js解析啥的一些东西,模糊理解词法分析、语法分析的概念后,才发现js函数需要完全把上面那张图给大卸八块、分而食关键字、函数名、实参、形参、局部变量等才能搞清楚函数是什么。好了这些逗逼过程没必要写,回到上图:

作用域链:正常情况各层作用越内部是一幅图景:凡是带var申明的变量(首先以undefined为赋值)、带function申明的函数,上浮到各层作用域的天花板。运行时内层作用域的代码开始运行,先向本层作用域顶部抓需要的变量,没抓到就跳到外一层去抓,直到全局作用域那层。机制上就是酱紫(的吧)。

反之需:倘若要从外层抓内层变量怎么办?

——目测这个命题可以归纳出一大串解决办法来,但这里只说闭包。再上图(看NO.1):

看看NO.1函数f1就是上述命题的一个解决方案,通过return语句将f2的值返回到f1所在的执行环境中,一个函数不但要执行某些动作,还会返回一个指定结果,于是外层变量result只要接受f1的结果就成功获得函数内部变量的值了。

a little more,如果将f2定义为一个内部函数,就是NO.2表示的那样,内部函数f2能抓取外部函数f1里的局部变量x,随着f1return功能将它局部变量x抛到了外层环境当在外层环境调用内部函数f2(通过result引用了 f1( )执行结果即函数f2,并最终result( )一下时,闭包就形成了。同样它也提供了一个解决方案,可以在函数之外抓取到函数内部的局部变量。一言以蔽之,闭包是一个内部函数,它在包含它的那个函数之外也能访问到这个包含函数的局部变量。


闭包的重要的用途

——就是解决上面的那个命题啦:从外部抓取局部变量的值,同时它能将局部变量的值保持在内存中——函数执行后不像一般情况那样局部变量会当做垃圾回收,闭包能够让运行过后的那些局部变量仍然保留“余热”。为什么呢?就NO.2来说,f2被赋给全局变量result后,因全部变量不会被内存回收,那么result装的引用也不会丢失,而f2的存在是依赖于父函数f1的,所以它们整个干脆都保留下来了。再上一图,来探讨一些闭包的问题(不知是否准确,希望高手不吝赐教):


Pro1:var r1 = outer( )中outer( )执行结果是什么?会不会让inner函数中的变量i自增1了呢?不会。分析如下:

首先outer函数确实执行了,

其次,如果剥开function outer( ){…}这个外衣忽略不看的话,内部的第1、2行都是在申明,你会发现并没有inner函数被调用的语句,所以在outer函数体内inner函数仅仅作为一个申明而存在。[将outer( )结果赋值给r1时只会模拟其表达式的结果但不会真正执行函数里面的++i和++n]这句话是错的。可以进一步模拟证实一下:

function outer() {
  var i = 0;
  function inner(n) {
    ++n;
    alert(n);
  }
  inner(2);        //内部执行一次
  return inner;
}
var r1 = outer( );  //3

最后,返回值是一个函数名inner,指向inner函数。最后将inner指针交给全局变量r1。

Pro2:两次调用r1( n )其n值怎么变化?首先将全局n=1递给r1函数的实参n,r1(1)的执行首先进入到最深1层作用域,形参n被传值=1,而i沿着作用域链从每层的天花板抓取它自己的值,在第2层抓到i=0就ok了,++n得到执行,n=2。

          第二次调用调用r1( n )时,n被刷新一次,将重复第一次的变化,最终n=2,由此可见闭包inner的参数是不会被保持的。但是i这次抓值是在inner函数内部就抓到了i=1,自增后=2。以下作为证明:

 function outer() {
    var i = 0;
    function inner() {
      console.log(++i);    //i在哪抓值?
    }
    return inner;
  }
  var r1 = outer( );
  alert(r1( ));  //undefined  1    *inner返回值被弹出为undefined,console.log结果为1
  alert(r1( ));  //undefined  2    *同时分析

第二次调用r1( )因为内部函数inner开始,第一次调用保存下来的i=1被保留在自身内部。第二次调用则会直接使用该i值。其实不难理解,outer函数内部的局部变量、参数、其他申明函数都会因为闭包而保存在内存中,同样函数inner也不会被回收,而是保存下来。但是所有这些内部之进一步内部函数的局部变量却不会被保存。


Pro3:从同一个函数产生的2个闭包,它们是如何独立的?不独立肯定不行,如r2同样是对inner函数的一个引用,运行一下r2(1)后重演了r1函数的故事,并没有利用到上述闭包函数内外的“余热”。独立是必须的,但why?这是因为:

var r1 = outer( ); 

var r2 = outer( ); 

相当于两次执行outer函数,outer执行干了神马?——读它的代码,当然是每次都申明了一个inner函数咯,并将申明的函数引子给交到外部环境来,两个inner函数它们是两次函数执行的执行环境中的产物,如果两次执行环境相互独立,那么它们也会是相互独立的……(好像已经论述不清了。。。在这先存疑,愿后续能求证释疑


//有些头大,闭包暂且学到这里~



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值