闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。- 《Javascript高级程序设计(第四版)》
注意:匿名函数不是闭包
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。 - MDN
定义
先大概了解一下,闭包,我推荐去看这篇文章,参考,Js闭包的原理(图解)- Marhoo🐝,看完就懂,无需多言
这篇文章大概讲了闭包,它是引用外部作用域之后导致作用域链没有被断开,无法被 GC
的一个原因产生了闭包
注意:需要充分理解这部分知识还需要去了解一下垃圾回收,作用域链,活动对象的相关知识
注意
- 嵌套函数的作用链和活动对象,在外部函数执行时就会创建,闭包也就是在这个时候创建
原理
闭包的实现原理,其实是利用了作用域链的特性,在当前执行环境下访问某个变量时,如果不存在就一直向外层寻找,最终寻找到最外层也就是全局作用域,这样的一个链条就是作用域链,而闭包就是这样,它引用了另一个函数作用域中变量
闭包的优点及应用场景
优点
- 隐藏变量,避免全局污染
- 可以读取函数内部的变量
应用场景
- 函数防抖节流,参考函数防抖节流场景
- 单例模式,比如,消息提示(
message
)组件 - 模块化
闭包的缺点及解决
缺点
内存泄露
函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长,为什么没有被垃圾回收机制处理?看下面代码
function fun() {
let a = 0;
return function () {
console.log(a++);
};
}
let foo = fun();
foo();
foo
引用着 fun()
返回的匿名函数,这个匿名函数引用了另一个函数作用域中变量,产生了闭包,因此无法消除,理论上来说是因为 foo
引用匿名函数导致无法被垃圾回收,所以闭包的解决方法如下
function fun() {
let a = 0;
return function () {
console.log(a++);
};
}
let foo = fun();
foo();
foo = null;
但实际上在 node
环境中测试,手动垃圾回收后,内存并没有被回收(相关测试可以参考我的另一篇文章,掘金好像没有)
解决方法
解决闭包的方法
- 能不用闭包就不用
- 及时释放
实战
结合全局对象(window
)、活动对象、作用域链、闭包去思考下面的代码片段
var name2 = "The Window";
var object2 = {
name2: "My Object",
getNameFunc: function () {
var that = this;
return function () {
return that.name2;
};
},
};
console.log(object2.getNameFunc()());
getNameFunc
返回的匿名函数(假设为 t
)中产生了闭包,存在于 window
对象中,因为 window
调用了这个匿名函数