文章目录
前言
相信大家在学习前端的路上都有接触到过闭包这个东西,并且这也是面试中的常客。那么闭包究竟是个什么呢?让我们一起来看看吧。
一、闭包是什么?
函数内部的变量不会影响到外部作用域,并且父函数返回的子函数可以访问到其父函数作用域的变量。
换句通俗的话说呢,就是一个作用域能访问另一个作用域的变量,且局部变量会常驻在内存中不会被回收。
二、闭包有什么好处和坏处呢?
1.好处
1.可以让外部函数访问内部变量。
2.能将局部变量存储在内存中达到重复使用。
3.可以避免使用全局变量造成全局污染。
2.坏处
会造成内存泄露(此处并不是绝对的,下面我将详细探讨闭包的内存泄露问题)
二、闭包会造成内存泄露吗?
1.前言
之前因为自己没有去实践过,所以看到网上说闭包会造成内存泄露,也就这么认为了。
在我学习了react hooks之后,我发现hook里面的useState就是利用闭包来实现每次渲染保持变量不变的,当时我就在想,如果闭包会造成内存泄露,那么react hooks里面不就会使用大量的闭包吗?这样的话性能也太差了吧?带着疑问,我查阅了书籍还有资料,发现了这个问题。
2.闭包会造成内存泄露吗,如果会为什么还会再react hooks中大量使用呢?
首先,闭包会造成内存泄露!!!但是只在ie8及之前会造成内存泄露。
现在的闭包已经不会造成内存泄露了。
三、为什么ie8及之前会造成内存泄露?
如果要探讨内存泄露这个问题,首先我们得先搞清楚,为什么会造成内存泄漏?
1.为什么会造成内存泄露
这一点得从垃圾回收机制说起,感兴趣的朋友可以去看我的另一篇专门讲垃圾回收机制的博客。
带你探索垃圾回收
首先,我们知道现在最常用的垃圾回收策略是标记清除,但在最早的时候,我们是使用引用计数来作为我们的垃圾回收策略的,但因为引用计数存在一些问题,所以被弃用了,但是,内存泄漏就是由引用计数造成的!!!!
2.引用计数的缺陷
引用计数的大概思路就是给声明变量赋给一个引用值,如果值被赋给另一个变量就+1,如果被覆盖就-1,当引用数为0时,就会释放这个值的内存。
引用计数存在了一个很严重的问题,循环引用。
循环引用的意思就是对象A有一个指针指向对象B,而对象B也引用了对象A,比如:
function problem() {
let objA = new Object()
let objB = new Object()
ObjA.test1 = objB
objB.test2 = objA
}
这个例子中,objA和objB通过各自的属性相互引用,导致引用数都是2.,在引用计数下,他们引用数永远不会到达0,如果函数被多次调用,则会导致大量内存永远不会被释放,从而造成内存泄露!
3.为什么ie8及之前还会存在引用计数呢?
在ie8及更早的版本中,并非所有对象都是原生js对象。BOM和DOM中的对象是C++实现的组件对象模型对象,简称COM对象。
COM对象是使用引用计数实现垃圾回收的,所以,即使这些版本的ie已经使用标记清除,但js存取的COM对象依旧是使用引用计数的。所以只要涉及到了COM对象,那么就会遇到循环引用的问题,比如:
let box = document.querySelector('.box')
let obj = new Object()
obj.box = box
box.obj = obj
在这个例子里,DOM对象和原生js对象之间造成了循环引用,所以DOM元素的内存永远不会被清除。从而造成内存泄漏
总结
在ie9之后已经全面改成了js对象啦,这避免了由于存在两套垃圾回收算法而导致的问题,还解决了内存泄露现象!
现在我们可以愉快的使用闭包啦!