摘抄:https://blog.csdn.net/Q846169253/article/details/81148290
闭包就是能够读取其它函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解为“定义一个函数内部的函数"
本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
function makeFunc(){
var name = "Mozilla";
function displayName(){
alert(name)
}
return displayName
}
const myFunc = makeFunc();
myFunc()
试一试结果:js中的函数形成了闭包,在本例子中,myFunc是执行makeFunc时创建的displayName函数实例的引用。displayName的实例维持了一个对它的词法环境(变量name存在与其中)的引用,因此,当myFunc被调用时,变量name仍然可用
function makeAdd(x){
return function(y){
return x + y
};
}
var a = makeAdd(5);
var b = makeAdd(10);
console.log(a(2)); //7
console.log(b(2)); //12
闭包的原理
借助函数的立即执行、参数以及函数的return返回值,多创建了一层作用域。从而实现外部函数持续被引用而不能释放内存空间,将值存储下来
闭包的作用
闭包就是将函数内部和函数外部连接起来的一座桥梁。使得外部函数可以读取内部函数的变量,这些变量的值始终保持再内存中,不会被垃圾回收器回收。
闭包如何从外部的作用域读取到内部作用域的东西
那就是在函数的内部,再定义一个函数。将内部函数传递到所在词法作用域以外
闭包的优点
- 避免全局变量被污染
- 方便调用上下文的局部变量
- 加强封装性
闭包的缺点
- 闭包常驻内存,内存消耗很大
- 可能导致内存泄漏
解决方案:在退出函数之前,将不使用的局部变量全部删除
如何删除?
// 普通变量:将变量置为undefined
var tt = 111;
tt = undefined;
//带有属性或者方法的, 用delete删除属性或方法
delete tt.name
内存泄漏
一块被分配的内存既不能被使用,也不能回收,从而影响性能,从而导致程序崩溃。
内存泄漏起因:一般使用技术系统来处理内存,每个对象根据被引用对象多少来计数,当计数为零时,该对象就会被销毁,释放内存。而当两个对象循环引用时,计数始终为1,则永久性地占着内存。而闭包实际上非常容易造成js对象的dom对象的循环引用
function ex(){
var element = document.getElementByID('div1');
element.onclick=function(){
alert("aaa")
}
}
ex()用匿名函数创建闭包,该DOM对象的onclick属性引用了匿名函数闭包,而闭包可以引用外部函数example()的整个活动对象,包括element;DOM(div1.onclick)--->JS(element) 由此形成了Javascript对象和DOM对象的隐藏循环引用。
造成的内存泄漏
解决方法:
在js代码段运行完之后将形成循环引用的js对象设置为空
function example(){
var element = document.getElementByID("div1");
element.onclick = function(){
alert("This is a leak!")
}
element = null; //添加的语句
}
垃圾回收机制
找到那些不再继续使用的变量,然后释放其占用的内存,垃圾收集器会按照固定的事件间隔周期性进行这一操作
两种方法———标记清除和引用计数
- 标记清除
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量的标记和被环境中的变量引用的变量的标记,此后,如果变量再被标记则表示此变量准备被删除
另一种解释:
当变量进入执行环境时标记为“进入环境”,当变量离开执行环境时则标记为“离开环境”,而标记为“离开环境的变量则可以被回收” - 引用计数
跟踪记录每个值被引用的次数,当声明一个变量并将一个引用类型的值赋值给该变量时,这个值的引用次数就是1,如果这个值再被赋值给另一个变量,则引用册数加1. 相反,如果一个变量脱离了该值的引用,则该值引用次数减1,当次数为0时,当次数为0时,就会等待垃圾回收器的回收。