---------------------这是学习笔记---------------------javascript
随着前端业务需求的不断增多,相比之前,咱们会占用更多的内存。可是内存并非无限的,而对于那些咱们再也不须要的变量、对象该怎么处理呢?难道一个一个去手动释放么?其实并不须要,Javascript 具备自动垃圾回收机制,会按期对那些咱们再也不使用的变量、对象所占用的内存进行释放前端
Javascript 会找出再也不使用的变量,再也不使用意味着这个变量生命周期的结束。Javascript 中存在两种变量——全局变量和局部变量,所有变量的声明周期会一直持续,直到页面卸载java
而局部变量声明在函数中,它的声明周期从执行函数开始,直到函数执行结束。在这个过程当中,局部变量会在堆或栈上被分配相应的空间以存储它们的值,函数执行结束,这些局部变量也再也不被使用,它们所占用的空间也就被释放数组
可是有一种状况的局部变量不会随着函数的结束而被回收,那就是局部变量被函数外部的变量所使用,其中一种状况就是闭包,由于在函数执行结束后,函数外部的变量依然指向函数内的局部变量,此时的局部变量依然在被使用,因此也就不可以被回收浏览器
function func1 () {
const obj = {}
}
function func2 () {
const obj = {}
return obj
}
const a = func1()
const b = func2()
复制代码
上面这个例子中,func1
执行时为 obj
分配了一块内存,可是随着函数执行结束,obj
占用的空间也就被释放了;而 func2
执行时,也为 obj
分配了内存,可是因为 obj
最终被返回赋值给了 b
致使其依然被使用,因此 func2
中的 obj
占用的内存不会被释放bash
垃圾回收有两种实现方式,分别是标记清除和引用计数闭包
当变量进入执行环境时标记为“进入环境”,当变量离开执行环境时则标记为“离开环境”,被标记为“进入环境”的变量是不能被回收的,由于它们正在被使用,而标记为“离开环境”的变量则能够被回收函数
function func3 () {
const a = 1
const b = 2
// 函数执行时,a b 分别被标记 进入环境
}
func3() // 函数执行结束,a b 被标记 离开环境,被回收
复制代码
统计引用类型变量声明后被引用的次数,当次数为 0 时,该变量将被回收学习
function func4 () {
const c = {} // 引用类型变量 c的引用计数为 0
let d = c // c 被 d 引用 c的引用计数为 1
let e = c // c 被 e 引用 c的引用计数为 2
d = {} // d 再也不引用c c的引用计数减为 1
e = null // e 再也不引用 c c的引用计数减为 0 将被回收
}
复制代码
可是引用计数的方式,有一个相对明显的缺点——循环引用ui
function func5 () {
let f = {}
let g = {}
f.prop = g
g.prop = f
// 因为 f 和 g 互相引用,计数永远不可能为 0
}
复制代码
像上面这种状况就须要手动将变量的内存释放
f.prop = null
g.prop = null
复制代码
在现代浏览器中,Javascript 使用的方式是标记清楚,因此咱们无需担忧循环引用的问题
1)全局变量照成内存泄露
function fn() {
name = "你我贷"
}
console.log(name)复制代码
在 JS 中处理未被声明的变量, 上述范例中的会把 name , 定义到全局对象中, 在浏览器中就是 window 上. 在页面中的全局变量, 只有当页面被关闭后才会被销毁. 因此这种写法就会形成内存泄露, 固然在这个例子中泄露的只是一个简单的字符串, 可是在实际的代码中, 每每状况会更加糟糕.
另一种意外建立全局变量的状况.
function fn() {
this.name = "你我贷"
}
console.log(name)复制代码
在这种状况下this被指向了全局变量 window, 意外的建立了全局变量. 咱们谈到了一些意外状况下定义的全局变量, 代码中也有一些咱们明肯定义的全局变量. 若是使用这些全局变量用来暂存大量的数据, 记得在使用后, 对其从新赋值为 null.
2)未销毁的定时器和回调函数照成内存泄露
function fn() {
return 2
}
var oTxt = fn();
setInterval(function() {
var oHtml = document.getElementById("test")
if(oHtml) {
oHtml.innerHTML = oTxt;
}
}, 1000); // 每 1 秒调用一次复制代码
若是后续 oHtml 元素被移除, 整个定时器实际上没有任何做用. 但若是你没有回收定时器, 整个定时器依然有效, 不但定时器没法被内存回收, 定时器函数中的依赖也没法回收. 在这个案例中的 fn 也没法被回收.
3 ) 闭包照成内存泄露
在 JS 开发中, 咱们会常常用到闭包, 一个内部函数, 有权访问包含其的外部函数中的变量. 下面这种状况下, 闭包也会形成内存泄露.
3)DOM 引用照成内存泄露
不少时候, 咱们对 Dom 的操做, 会把 Dom 的引用保存在一个数组或者 Map 中.
var elements = {
txt: document.getElementById("test")
}
function fn() {
elements.txt.innerHTML = "1111"
}
function removeTxt() {
document.body.removeChild(document.getElementById('test'));
}
fn();
removeTxt()
console.log(elements.txt)复制代码
上述案例中, 即便咱们对于 test 元素进行了移除, 可是仍然有对 test 元素的引用, 依然没法对齐进行内存回收. 另外须要注意的一个点是, 对于一个 Dom 树的叶子节点的引用. 举个例子: 若是咱们引用了一个表格中的 td 元素, 一旦在 Dom 中删除了整个表格, 咱们直观的以为内存回收应该回收除了被引用的 td 外的其余元素. 可是事实上, 这个 td 元素是整个表格的一个子元素, 并保留对于其父元素的引用. 这就会致使对于整个表格, 都没法进行内存回收. 因此咱们要当心处理对于 Dom 元素的引用.