对闭包产生的理解
总结,产生闭包的条件
1. 函数嵌套(注意这里提到的函数是指以function来定义的,不包括setInterval()等这种以非function()函数的函数)
2. 嵌套的内部函数引用外部函数的变量或者是函数(也就是所有的变量类型:基本变量类型和引用变量类型都包括),注意这里是用的“引用”,这意味着内部函数只需要有外部函数的局部变量,注意是外部函数内部的局部变量就行,不需要去调用内部函数,但是必须要执行内部函数的定义(并不需要执行内部函数(不需要执行内部函数的函数块),"调用"和“引用”在这里是有区别的),执行内部函数定义,可以通过调用外部函数来实现,因为调用外部函数结合执行上下文(变量或者是函数的提升),可以实现内部函数定义的执行
3.执行内部函数的定义
如何看产生了几个闭包:
方法一:看内部函数被创建了几次(注意是内部函数被创建了几次,而不是内部函数被调用了几次)
方法二:看外部函数被“调用了”几次
闭包视频例题
例题一1:
例题二:
上面产生了闭包
但是,下面的这个地方,就没有产生闭包
不满足上面产生闭包的第二个条件:“引用”
下面的实例
注意,上面的f()是可以执行闭包函数,,并不是又创建了闭包函数,执行和创建还是有区别的
闭包的作用
因为上面闭包产生的条件,谈到了函数的嵌套,既然是函数的话,那么函数里面的变量就是局部变量,局部变量是有生存周期的,一般的局部变量当对应的函数完毕后,会自动释放存放该局部变量的内存,但是,闭包其中的一个作用,就能很好的解决“局部变量生存周期的问题”
其二,作用域的问题,它的作用是可以隔离变量,使得全局作用域里面不能调用局部作用域的变量,但是局部作用的变量是可以访问全局作用域或者是上一级的局部作用域的同名变量的,第二: 不同作用域下同名变量不会有冲突
作用域链是用来找变量的,和原型链(隐式原型链)用来查找属性是类似的
对闭包的进一步地思考
对上面第一个问题的思考
下面用红圈圈出来的都是局部变量,那么是不是在函数fn1执行完毕后,是不是就都被释放了呢?a没有被释放,因为它还在闭包中("闭包"最好还是理解为存在于嵌套函数的内部函数中),fn3这个变量被释放了,fn2这个变量也被释放了,但是值得注意的是fn2原本指向的对象也不存在了,因为不存在其它的引用类型变量指向fn2之前指向的这个函数对象了,虽然,fn3这个局部变量被释放了,但是在此处fn3所指向的函数对象依然存在,并不是垃圾对象,因为有这句话的存在var f = fn1(),这意味着现在fn3所指向的对象现在被f这个全局变量指向着在,这进一步意味着,当如果没有var f = fn1()这句话的话,那么fn3所指向的函数对象还存在吗?(显然,不存在,那么这进一步地意味着,闭包也将不存在,进一步地意味着原本存在于闭包中的变量,也将不存在,因为闭包存在于fn3所指向的对象中,而局部变量a存在于闭包中)
特别注意的是,上面的fn1是一个全局变量,函数变量名并不一定是局部变量,而且指向函数对象的函数变量名由于是局部变量被释放后,该函数变量名所指向的函数对象不一定就是被释放了,对象是否为垃圾对象,要看有没有指向该对象的“引用”,也就是指向该对象的变量
问题二
闭包中的变量能直接被外部看见,也就是直接被外部调用吗?举个例子是,直接在外部操作a–不行,因为闭包中的变量实际还是一个局部变量,我们可以在全局作用域中,直接去访问一个局部变量吗?对这个局部变量进行“读或者是 写的操作吗”,显然,这是不行的,但是,闭包有“在全局作用域下,间接操作闭包中的局部变量的
闭包的应用二
JS利用闭包产生Js模块,通过return方法来实现的方式
js模块中返回多个属性或者是方法(用到对象,因为对象里面可以包含多个数据类型的数据),
下面展示了“向外暴露一个包含n个属性或者是方法的对象或者是函数”的方法(方式二),思路是:将包含闭包的函数对象的地址封装到一个对象了里面,然后赋值给一个window的属性(相当于全局变量),结合匿名函数的自调用(“IIFE(立即执行函数)”)的特点,直接实现了,不需要像第一种方式那样,需要在别的地方去调用外部函数,才能激活闭包
方式二,虽然好,但是从“代码压缩(可以将局部变量用单个字母来表示,提升效率)” 的角度来看的话,下面这种方式更好,方式二,带来了一个值得思考的问题,函数内的变量一定是局部变量吗?(显然,不是),当这个函数内部的变量,不是在函数内部用var 定义的话,而是利用window.变量名,让这个变量成为全局对象window的属性,这个就相当于成为了全局变量,为什么呢?(理由很简单,当在函数局部或者是全局调用这个变量时,会根据作用域去找,直至找到window这个全局作用域)
上面这样处理之后,进行“代码压缩”之后,就变成了这样
思考一个问题,我们想要存一个私有是数据,该怎么处理呢?首先,可以直接用一个对象来存放数据吗?不行,因为,它不是私有的,但是在函数内部创建数据的话,就能够使得这个数据变为私有的(私有数据)
闭包的缺点
内存溢出与内存泄漏
下面中的变量a虽然位于函数内部,但是,它不是局部变量,因为fun函数执行完毕后,变量a不会被自动释放,因为,它实际上是一个全局变量,另外特别注意的是,这个并会报错,因为它赋了值,什么时候会由于变量没找到会报错,沿着作用域链找完了,都没有找到的话,就会出现由于变量没找到时而报错