闭包的概念
在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
上面的概念取自于维基百科,上述概念中其实蕴含了两种概念
闭包的定义在js学习中一直是非常模糊的,可以说每个人对于闭包的理解是不同的,我认为闭包的是作用域的产物,在JavaScript中,根据词法作用域的规则,内部函数总是可以访问其外部函数当中声明的变量;当调用通过外部函数返回的内部函数时,即使此时外部函数已经执行结束,但是内部函数所引用外部函数的变量依然保存在内存中,我们把这些变量的集合称为闭包
function foo (){
function bar (){
console.log(aaa)
}
var aaa =123;
return bar
}
var demo = foo()
demo() // 123
在上述的例子,输出的是123,但是根据我们对作用域的理解,aaa属于函数内部作用域中的变量,而demo的运行环境处于全局作用域,按理说外部环境并不能访问内部函数的变量。很多小伙伴都应该明白,这是运用了闭包,那么接下来我们需要了解为什么会产生闭包而闭包又是何时产生的。
进一步了解闭包
根据前面作用域的学习,我们可以了解上述例子的执行过程,这里我简单将其列出:
1.foo函数定义,形成foo函数的作用域链,作用域链的第一位为全局执行上下文
2.foo函数执行,作用域的第一位,改为foo函数的局部执行上下文,第二位则是之前的全局执行上下文。在foo函数执行的同时,我们迎来了bar函数的定义。形成了bar函数的作用域链(当访问一个变量时,解释器会首先在当前作用域中查找标识符,如果没找到,就去父作用域找,直到找到该变量的标识符或没找到为止。这条寻找的链路就叫作用域链),由于bar在函数foo内被定义,它可以访问foo的作用域,使以bar的作用域链上天生就有foo的作用域链,所以下面我将bar的作用域链指向foo的作用域链。同时bar函数被return赋值给了demo这个全局变量。
3.bar函数执行,bar函数作用域链第一位改为自身局部执行上下文,执行 console.log(aaa) 按照作用域链查找,输出123
foo函数执行完毕之后,js自带的垃圾回收机制,会将其标记,并在下次回收时,释放foo函数所占用的内存空间,但是好巧不巧,你需要释放的作用域链,已经作为bar的作用域链的一部分被带到外部了,那指定不能让你销毁呀。
这时闭包就产生了,在我的理解中,bar被foo返回时就产生了闭包,此时内部函数和外部函数同处于一个作用域下,bar的作用域链上绑定了foo的作用域链,导致foo函数作用域链不能释放。