闭包&内存泄漏
闭包
就是可以在在一个函数的内部可以访问到函数外部的变量。这是因为内部函数对外部函数属于同一作用域内,通过闭包内的函数访问到变量是因为内部函数保持着对变量的引用,当注册一个点击事件的时候,就是一个闭包,当点击事件完成的时候,还会对改变量保持着引用
闭包代码
var makeCounter = function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } }; var Counter1 = makeCounter();
上面是一个简单的闭包栗子,总所周知,引用对象存储的位置是堆内存中,当 js 的垃圾回收机制检测不到该变量被使用,就会被垃圾回收机制收走。而闭包,就会产生一个一直存在,切不释放的变量
内存泄露
定义:一块被分配的内存既不能使用,也不能回收。从而影响性能,甚至导致程序崩溃。
起因: JavaScript 的垃圾自动回收机制会按一定的策略找出那些不再继续使用的变量,释放其占有的内存。然而由于一些原因导致在这种机制下内存管理器不能正确解读 JavaScript 变量的生命周期,从而没有释放其内存,而也没有再被使用。
js 造成内存泄漏的几种情况
js 的垃圾回收机制
js 的垃圾回收机制 (gc) 就是为了防止内存泄漏的,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。所以这里又涉及到变量的生命周期,当一个变量的生命周期结束之后它所指向的内存就应该被释放。 JS 有两种变量,全局变量和在函数中产生的局部变量。局部变量的生命周期在函数执行过后就结束了,此时便可将它引用的内存释放(即垃圾回收),但全局变量生命周期会持续到浏览器关闭页面。所以当我们过多的使用全局变量的时候也会导致内存泄漏的问题。
js 用了两种策略,一个是标记清除法,另一种是引用计数法,其实他两的实现原理我们只明白一点就是都是通过判断当前的变量是否被引用,如果没有被引用,就说明该变量应该被回收,怎么回收就是上边说得两种策略的事情了
主要关注的代码点
1.DOM 中的 addEventLisner 函数及派生的事件监听, 比如 Jquery 中的 on 函数, vue 组件实例的 $ on 函数,第三方库中的初始化函数
2.BOM 对象的事件监听,比如 webSocket 的监听事件
3. 避免不必要的函数引用
4. 如果是要 render 函数,避免在 html 标签中 DOM / BOM 事件
在 vue 中如何处理内存泄漏的
1. 如果在 mounted / created 钩子中绑定了 DOM / BOM 对象中的事件,需要在 beforeDestroy 中做对应解绑处理
2. 如果在 mounted / created 钩子中使用了第三方库初始化,需要在 beforeDestroy 中做对应销毁处理
3. 如果组件中使用了定时器,需要在 beforeDestroy 中做对应销毁处理
4. 模板中不要使用表达式来绑定到特定的处理函数,这个逻辑应该放在处理函数中
5. 如果在 mounted / created 钩子中使用了$ on ,需要在 beforeDestroy 中做对应解绑 ( $ off) 处理
6. 某些组件在模板中使用 事件绑定可能会出现泄漏,使用$ on 替换模板中的绑定
另外, vue 在 IE edge 浏览器下,父子组件的场景,子组件依赖父组件的状态,子组件控制父组件状态变化从而反馈给子组件的展示变化,子组件通过 v - if 模式存在于视图中,父组件通过状态控制子组件的 v - if 状态变换。子组件控制父组件状态完成子组件数据填充后,父组件切换子组件的 v - if 状态,子组件占用 dom 结构被清理。此时,子组件存在时的内存占用未被释放,当父组件再次回切 v - if 状态时,子组件重新展示,内存飙升,重复几次切换后,内存飙升明显,页面卡顿