刚开始接触这个词的时候,我是一头雾水的,我尝试的去理解他。我开始到处查找关于它的解释,在很多篇文章里面,都把他理解为函数。但很多文章中这句话的原话都是“闭包可以理解为一个函数”,而这个函数指的就是在一个函数体内定义的另一个函数,既然说的是“可以”,那说明闭包的本质不是函数,于是我把闭包的各种特性进行分析,尝试彻底搞清楚闭包到底是个什么“东西”。
说起闭包,就必须提到js中作用域的问题。js中变量可以分为全局变量和局部变量,全局变量在任何地方都能够访问到,而局部变量只能够在当前作用域内访问到。
function fun1(){
var n = 999;
}
function fun2(){
console.log(n);
}
比如在上面这个例子中的n是一个局部变量,函数fun2没办法读取到n,如果运行函数,会报错。那如何解决这个问题呢?
function fun1(){
var n = 999;
function fun2(){
console.log(n);
}
}
将fun2函数放到fun1函数体内,这样一来就能够访问到fun1函数中的局部变量n了。这一点是闭包三个特性中的一点,加上这点三点特性分别为:
- 函数嵌套函数
- 函数内部可以引用函数外部的参数和变量
- 参数和变量不会被垃圾回收器回收
这里提到垃圾回收器,简单介绍一下
js中有一个垃圾回收器程序,专门用于清理内存它有2中运行方式,分别为标记清除和引用计数。
标记清除:当变量进入执行环境的时候,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候将其标记为“离开环境”,被标记为离开环境的变量将会被释放
引用计数:当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加1,反之则减1,如果一个内存空间没有再被引用时,也就是引用次数为0时,该内存空间被释放。
回到闭包的特性,为什么说参数和变量不会被回收呢?
如图,在fun2出栈后,虽然它的活动对象被释放,但是由于fun2函数中需要用到fun1中的变量,所以fun2()的scope属性仍然指向fun1的AO,而fun1函数的活动对象中存在fun2,指向fun2函数。所以形成了闭包,这样一来,变量n得到了保护。
综上所述,可以总结出闭包的优点:
- 保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
- 在内存中缓存一个变量
- 匿名自执行函数可以减少内存消耗
同时,它的优点暴露除了它的缺点。因为保护到了变量不被回收,但同时会占内存,所以容易造成“内存泄漏”。关于内存泄漏,是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。(这个名词容易对初学者造成歧义,比如说我,所以特地去百度了一下具体解释)。
通过对闭包的应用,优缺点等的分析,闭包可以说是一种方法,解决问题的方法,也可以说是桥梁,连接函数体内外的桥梁,也同样可以理解为,在函数体内定义的另一个函数。闭包是抽象的,对于我们,只需要理解在后台的运行方式即可。