闭包的本质
概念
闭包函数:声明在一个函数中的函数,叫做闭包函数。
闭包:内部函数总是能访问到外部函数声明的参数和变量,即使在外部函数返回后(寿命终结)了之后
特点
1.让外部访问函数内部变量成为可能;
2.局部变量会常驻在内存中;
3.可以避免使用全局变量,防止全局变量污染;
4.会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
闭包的创建
闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,**每次外部函数执行的时 候,外部函数的引用地址不同,都会重新创建一个新的地址。**但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。
上面是什么意思呢:假设你有fun()函数,里面有声明变量a并且返回闭包函数b(),闭包函数调用了a,
那么你调用两次f1=fun()和f2=fun(),f1与f2指向的地址是不一样的,两次对fun()引用是独立的,也就是说变量a是独立存在的,f1对a的操作不会影响f2的a,
本来不考虑闭包,当函数return后,内部的数据应该被删除,也就是说a应该被删除,摧毁a的内存地址,但是如果闭包函数b()调用了a,那么a就不会被删除,而是将内存地址给闭包函数。
function outerFn(){
var i = 0;
function innnerFn(){
i++;
console.log(i);
}
return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2(); //1 1 2 2
闭包内存泄漏
首先分析下
函数的作用域链
1.创建函数outerFun()时,会创建一个预先包含全局变量对象的作用域链 ,保存在内部的[[Scope]]属性中。
2.调用函数outerFun()时,为此函数创建一个执行环境。
3.然后复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。(全局变量对象的作用域链 )
4.此后,创建一个活动对象 ,推入执行环境作用域链的前端([0]位置)。
此时执行环境的作用域链中包含两个变量对象:全局变量对象(第3步) 、 局部活动对象(第4步)。
注意如果全局变量与局部变量重名,只能通过window.a的方式访问全局
js变量搜索(先局部,后全局;先解析,后赋值)
函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。函数执行完后,局部活动对象被销毁,内存中仅保存全局作用域。
再来分析
闭包的情况
内部函数 被 外部函数 包含时,内部函数会将外部函数的局部活动对象添加到自己的作用域链中
outerFun(outerArgument){
//被包含的内部函数可以访问外部函数的变量
return function(){
return outerArgument+1
}
而由于内部匿名函数的作用域链 在引用 外部包含函数的活动对象 ,即使outFun执行完毕了,它的活动对象还是不会被销毁!
即,outerFun的执行环境作用域链都销毁了,它的活动对象还在内存中留着呢。
并且根据垃圾回收机制,被另一个作用域引用的变量不会被回收。
所以,除非内部的匿名函数解除对活动变量的引用(解除对匿名函数的引用),才可以释放内存。
// 创建函数 还未调用
var creatFun = outerFun(7)
// 调用函数
var result = creatFun(8)
// 解除对匿名函数的引用
creatFun = null
闭包会造成内存泄漏,就是因为它把外部的包含它的函数的活动对象也包含在自己的作用域链中了,会比一般的函数占用更多内存
结论:闭包找到的是同一地址中父级函数中对应变量最终的值
最后推荐:
https://blog.csdn.net/weixin_43586120/article/details/89456183
这篇博客包含很多经典闭包题