闭包是JavaScript中函数的一种高级应用方式,学起来有点复杂,所以想弄懂js闭包,我们需要先理解一下函数的两个阶段(定义阶段和调用阶段)。
定义阶段
1、在内存中开拓一个存储空间
2、把函数内的代码当做字符串一模一样地放在这个空间中(所有变量都不进行解析)
3、把这个空间地址赋值给函数名(变量名)
调用阶段:
1、按照函数名(变量名)找到对应的存储空间
2、重新开拓一个函数执行空间
3、在执行空间里进行形参赋值
4、在执行空间里进行预解析
5、把函数存储空间的代码复制一份到执行空间里面执行一遍
6、执行完毕之后,执行空间销毁
如下图所示:
函数的两个阶段
在特殊情况下,会出现一个不会销毁的函数执行空间——就是当函数内部返回一个复杂数据类型,并且在函数外部有变量接收这个复杂数据类型,这个时候函数的执行空间不会被销毁;当外部接收的这个变量不再引用函数内部的返回值时,这个函数空间就销毁了(如下图所示)。
一个不会销毁的函数执行空间
弄懂函数这些知识点之后,再来学闭包就不难了。首先,形成闭包要符合这三个条件,而且三者缺一不可:
1、函数A内部直接或者间接返回一个函数B
2、B函数内部使用着A函数的私有变量
3、A函数外部有一个变量接收B函数
function?a()?{? ? ?
//?这个?num?变量就是函数?a?的私有变量??????
????var?num?=?100 ;? ? ?
????return?function?b()?{? ? ? ?
????????console.log(num);? ? ?
????}????
}????
//?res?接受的是?a?函数执行以后的返回值????
//?res?接受的就是函数?a?内部返回的一个复杂数据类型(函数b)????
//?导致函数?a?的执行空间不会销毁????
var?res?=?a();
//?从现在开始,?res?随时可以是一个?函数a?里面返回的?函数b????
// res?随时可以调用????
res()????
//?当?res?调用的时候,?打印?num????
//?打印出来的就是?a?函数内部的私有变量?num?的值
这里面会涉及到少量概念:我们把这个不会销毁的A函数的执行空间叫做“闭包空间”;把A函数里面返回的B函数叫做“A函数的闭包函数”;所以闭包的官方定义也指函数内部的函数。
接着说一下闭包主要的三个特点:
1、延长了变量的生命周期;这个特点的好处就是变量会一直存在,但是因而也成了一个致命的缺点:就是这个函数的执行空间不会被销毁,而当一段内存空间中有一个不会被销毁的东西一直存在,就会出现内存占用,假如过多的话,还会导致内存溢出,结果引起内存泄漏。
2、可以访问函数内部的私有变量;利用这个特点,我们可以在函数外部访问到内部的数据。
3、保护私有变量;这是所有函数都具有的特点之一,普通函数的内部变量是不能在外部访问的,但是闭包空间可以。
可以借助这张图片进行了解:
闭包
理解了闭包的特点之后,我们即可以在需要延长变量公告周期的时候,或者者需要访问某个函数内部私有数据的时候,用闭包函数来处理问题啦,比方:循环绑定事件。
循环绑定事件
需要注意的是,闭包尽管有好处,但是假如我们有其余办法,还是尽量不要使用闭包函数,由于它的缺点是致命的哟!(慎用)