一、学习背景
肯定很多人学习js的过程中对闭包的了解一直处于懵懵懂懂的状态,对于闭包只是简单的了解“声明在一个函数中的函数,叫做闭包函数”,其实并不能很好的去理解他。这里已js运行的机制出发,深入探讨一下闭包产生的原因。
二、闭包的概念
这里引用百度百科的解释:
简单的理解就是:声明在一个函数中的函数,叫做闭包函数。
三、闭包的特点
1.让外部访问函数内部变量成为可能;
2.局部变量会常驻在内存中;
3.可以避免使用全局变量,防止全局变量污染;
4.会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
四、闭包的形成
深入了解闭包需要扎实的js基础。这是一段经典的闭包代码。首先定义一个funA函数,内部返回一个匿名函数。然后将funA函数赋值给变量b,这里b其实已经是funA函数的返回值,也就是匿名函数;然后执行b函数(这里其实也就是执行了funA函数里的匿名函数)。匿名函数是打印出一个变量a,对应的就是10这个值。
五、深入思考
是什么js的运行机制导致了这种情况?
这里涉及俩个概念 预编译产生的作用域 和 js的垃圾回收机制
1.预编译产生的作用域
第一作用域问题分为 局部作用域(Activation Object 局部作用域)和 全局作用域(Global Object 全局作用域),也就是常说的GO和AO。
GO执行过程
1.寻找变量声明
2.寻找函数声明并赋值
3.执行
上面的代码中首先找到a变量,(注意:这里是使用的var声明的,所以会有变量提示,但是赋值不提升,但在GO的执行过程中,会首先寻找变量声明,所以不管是let和const都是优先级最高的);此时的a变量会提升但赋值不提升所以是undefined,然后去寻找函数声明并赋值,这里又定义了一个a函数,所以此时GO中的a其实已经有值了,是一个a函数,最后js执行赋值操作将1赋值给a,此时var a = 1 ;这里也可以理解为什么函数声明优先级高于变量声明。所以在GO中变量a的赋值过程是 undefined => function a() => 1。
综上所述,那么第一次打印a是a函数,第二次是1
AO的执行过程
当一个方法被 调用 的时候(执行之前)会形成一个局部作用域AO
1. 寻找形参和变量声明
2. 实参值赋值给形参
3. 找函数声明,赋值
4. 执行
正是因为AO和GO产生的作用域链,导致函数销毁后外部仍有访问内部变量的地址,然后又因为js的回收机制,有引用或者标记的情况下,不会回收。所以这俩大特性产生了闭包。