//做一只蜗牛吧,一步一步往上爬;但傻逼才愿意,蜗牛速度那么慢…
//转行做个新手,发现写代码博客啥的真需要一股认真劲儿,在这里贡献一份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,随着f1的return功能将它局部变量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函数它们是两次函数执行的执行环境中的产物,如果两次执行环境相互独立,那么它们也会是相互独立的……(好像已经论述不清了。。。在这先存疑,愿后续能求证释疑)
//有些头大,闭包暂且学到这里~