刚刚总结完作用域链,我觉得很有必要马上对闭包总结一下,因为,之前也写过自己对闭包的理解,那时候只知道,闭包就是可以访问别的函数变量的函数,就是在函数里面的函数就叫做闭包,可是并没有深入探究,为什么,可以访问别的函数变量呢?直到,学习了JS的作用域链之后,就恍然大悟。
首先,来回顾一下,什么是作用域链吧。
作用域链就是由当前执行环境与上层环境的一系列变量对象组成,保证了当前执行环境对符合访问权限的变量和函数的有序访问。
那么什么是闭包呢?
闭包
关于闭包的定义,我在之前的文章里面已经讲过了,就是能够读取其它函数内部变量的函数
,那么为什么闭包可以读取其它函数内部的变量呢?这里面就涉及到了作用域链
。
demo01
var fn = null;
function foo(){
var a = 2;
function innerFoo(){
console.log(a);
}
fn = innerFoo();
}
function Bar(){
fn();
}
foo();
Bar();
在这个例子里面,正常来说,按照作用域链的规则,在foo()
函数的内部innerFoo()
函数可以访问到,父级foo()
的属性和方法,还有全局作用域下的属性和方法。这是因为,在innerFoo()
的作用域链里面包含了fooEC
和globalEC
的变量对象,所以有权限访问。
但是,之前讲过的垃圾回收机制
提到,如果foo()
方法执行完毕,应该释放内存,然后让垃圾回收机制回收,可是,为什么在Bar()
里面依然能访问到fn()
方法呢?
从上图可以看出,因为fn = innerFoo();
这句语句,使得全局作用域下的变量fn保存了innerFoo()
方法的引用,并且innerFoo()
方法是依赖于foo()
方法的,所以导致,foo()
方法里面的局部变量都保存在了内存里面,没有被回收,这也是使用闭包不当的一个弊端,会导致内存泄漏
。
当执行Bar()
方法的时候,Bar()
的作用域链中包含了全局作用域下的变量对象,所以可以访问到foo()
方法下的innerFoo()
,这就是闭包的一个底层原理。