闭包的产生
今天要说的内容是闭包,闭包在JavsScript的重要性不言而喻,无论你是初级的还是高级的工程师,你都避不开闭包这个知识点。
要想搞清楚闭包的产生,那就必须明白JavaScript另一个非常重要的基础知识点-作用域链,之前提到过一次了,为了比较系统的讲解,这里再次从作用域出发。
作用域与作用域链
作用域[[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不能访问,不能访问的属性仅供javascript内部调用,[[scope]]就是其中的一个。
[[scope]]指的就是作用域,其中存储了运行期上下文的集合(之前介绍的AO活动对象)
作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,这种链式链接就是作用域链。
看看如下代码:

代码的执行分两个步骤:
1)预编译
2)函数执行
在预编译环节会将全局变量字段组成一个Global Object(GO),而这个Global Object 会首先存入代码a函数的作用域里面(此时作用域里面只有一个Global Object),当代码运行时a函数会产生一个活动对象也就是运行时期的上下文,里面存有的字段属性都是a函数运行时确定的,看如下简易图解:
a函数运行前(通过预编译)

a函数运行后(产生AO)

当a函数运行产生AO后便形成了一个作用域链,a函数运行时如果找某个字段,首先会从自己的AO中查找,如果没有再到GO中查找,比如查找global字段
通过以上的讲解,想必明白一个函数的作用域是什么了吧,接下来要说的就是今天的主角-闭包
闭包的产生
有好多初学者可能不知道天天念叨的闭包是如何产生的,我们看下面的例子:

当a函数运行后,里面构成的作用域我们已经知道了,要明白AO的生命周期是在a函数运行完毕后自动销毁的,函数里面的return b;就宣布a函数运行完毕,而此时返回的是b函数的一个引用,这就等于又声明了一个b函数,注意b函数里面又用到了a函数里面的num字段,而a函数已经销毁了,这里就产生了闭包,我们做如下图解:
b函数声明:

b函数声明的数据正是a函数运行后的GO+AO,a函数运行结束后的销毁操作就是不再指向AO,回到最初声明的模样。
b函数运行:

从图中可以看到即使a函数销毁,b函数依然拿着a函数当时运行所生成的AO,当然包括里面的各种属性字段,此时已然形成了闭包。
现在来看看闭包的定义
闭包就是能够读取其他函数内部变量的函数。
知道什么是闭包了,接下来看一个小例子加深闭包的概念

输出结果:

b函数运行时,num++修改的是a函数运行时产生的AO里面的字段num;
c函数运行时,num--修改的也是a函数运行时产生的AO里面的字段num;
我们发现a函数运行所产生的AO成了公共的了,谁都可以调用修改,如下图:

以上便是闭包产生的由来以及一个关于闭包的小例子,接下来的问题是,我们在写代码的时候都有哪些涉及到了闭包,闭包有什么作用等等,这些我会在后面讲解,下一期我会讲到一个非常经典的例子,利用闭包接君闭包产生的问题。

浙公网安备 33010602011771号