一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情。
本题难度:⭐ ⭐
本题类型:综合题、JavaScript
内存管理
不管使用什么编程语言,都会有内存管理的概念。
内存管理包括分配内存,读写内存、释放内存,比如:
let name = '阿林' // 分配内存
console.log(name) // 读内存
name = 'lin' // 写内存
name = null // 释放内存
内存空间可以分为 栈空间 和 堆空间。
栈空间用来存储原始数据类型,存储的是固定大小的值。
堆空间用来存储引用类型,内存空间大小并不固定,需按引用情况来进行访问。
看下面的图例,你就能明白 JS 中变量在内存中是以怎样的形式存储的了。
let name = 'lin'
let age = 18
let arr = [1, 2, 3]
let obj = {
xxx: 123
}
对于分配内存和读写内存的行为,很多语言都较为一致,但释放内存的行为在不同语言之间有差异。
比如, JavaScript 有垃圾回收机制来自动管理内存的释放,释放内存不需要程序员操心。
但是某些语言,比如 C 或者 C++ 就需要自己去释放内存,给程序员带来了很大的负担,也是很多问题的来源。
但也并不是说 JS 有垃圾回收机制,在释放内存方面就万事大吉了,还是有很多内存泄漏的情况产生。
内存泄露场景举例
什么是内存泄漏?
内存泄漏是指内存中的变量没有使用了,但是没有被释放,存储着就白白浪费了内存。
下面,我列举一些内存泄漏的场景,方便大家理解。
DOM 元素
举个例子,页面中有一个id为 element 的元素。
<div id="element">test element</div>
引用了这个元素,赋值为 element,之后删除了这个元素。
let element = document.getElementById('element')
function remove() {
element.parentNode.removeChild(element)
}
删除之后再访问这个元素,这个元素还是存在,如下 gif 图所示:
为了解决这个问题,我们需要在 remove 方法中添加 element = null。
function remove() {
element.parentNode.removeChild(element)
element = null
}
事件监听
当事件监听器在组件内部挂载了事件处理函数,如果在组件销毁时没有主动清除,这个函数内部引用的变量或函数都不会被垃圾回收机制回收,如果引用变量存储了大量的数据,就白白占用了内存,造成大量内存泄漏。
拿 vue 组件举个例子。
// Home.vue
data() {
return {
msg: 'lots of home data'
}
}
created() {
window.addEventListener('resize', this.resizeCallback)
}
methods: {
resizeCallback() {
console.log('resize --> use', this.msg)
}
}
在 home 组件里监听了 resize 事件,用到了 home 组件里的数据,路由跳转到 about 组件里,home 组件已经被销毁了,但是 home 组件上被监听的方法和数据依然可以访问,如下 gif 图所示:
要解决这个问题也很简单,需要在组件被销毁时移除事件监听:
beforeDestroy() {
window.removeEventListener('resize', this.resizeCallback)
},
定时器
定时器和事件监听是一样的,一定要记得在合适的时机清除定时器。
data() {
return {
msg: 'lots of home data',
timer: null
}
}
created() {
this.timer = setInterval(this.intervalCallback, 500)
}
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
intervalCallback() {
console.log('setInterval --> use', this.msg)
}
}
全局变量
垃圾回收机制不会回收全局变量,全局变量使用完了之后只有手动清除。
所以尽量不要在全局变量上挂载大量数据,如果迫不得已在全局变量上挂载了很大的数据,也要记得在使用完了之后手动清除。
window.testArr = new Array(1000000).fill('xxx')
// do something
window.testArr = null
一些情况下变量会挂载到 window 上,在实际项目中应使用 lint 工具来避免,比如:
function fn() {
foo = '123' // 直接声明变量
this.bar = 'qwe' // 把变量赋给 this,this 指向 window
}
fn()
闭包
闭包的特性就是在函数内产生一些长期驻扎在内存中的变量,如果滥用闭包来存储一些很大的数据,就是白白浪费内存空间。
关于闭包,可以看我的这篇文章:
结尾
阿林水平有限,文中如果有错误或表达不当的地方,非常欢迎在评论区指出,感谢~
如果我的文章对你有帮助,你的👍就是对我的最大支持^_^
你也可以关注《前端每日一问》这个专栏,防止失联哦~
我是阿林,输出洞见技术,再会!
上一篇:
「前端每日一问(46)」随便打开一个网站,统计这个网站用过的标签总数
下一篇: