内存管理
内存为什么需要管理?
function fn() { arrList = []
arrList[100000] = 'lg is a coder' }
fn()
比如在执行这段代码时,就会导致内存占用一直上升,内存泄漏的问题
- 申请空间 let obj = {}
- 使用空间 obj.name = 'lg'
- 释放空间 obj = null
GC垃圾回收机制
如果调用完成,没有引用,应该被回收
引用计数算法
- 核心思想:设置引用数,判断当前引用数为0,则进行垃圾回收
- 引用计数器
- 引用关系改变时修改引用数字
优点:
- 发现垃圾立即回收
- 最大限度减少程序暂停 缺点:
- 无法回收循环引用对象
function fn() {
const obj1 = {}
const obj2 = {}
obj1.name = obj2
obj2.name = obj1
return 'asdas'
}
fn()
那么此时,虽然函数执行完成,但obj1中有引用obj2, obj2中引用obj1。循环引用,导致垃圾没法回收
- 时间开销大
常见的性能优化方法
- 尽量不使用全局变量,可以采取缓存全局变量的方式。
- 如果某个构造函数内部有成员方法,后面需要频繁调用。通过原型对象添加方法,比直接添加到构造函数内部,会很大程度节省性能。
- 避开闭包的陷阱:
function foo() {
var el = document.getElementById('btn')
el.onclick = function() {
console.log(el.id)
}
// el = null
}
foo()
如上面这段代码,btn在DOM中本来存在,而我们又通过 el 赋值的方式再次引用,就会产生2次引用。此时,如果DOM节点被移除,V8引擎的垃圾回收机制,就不能将它回收,因为还有闭包存在引用。
解决方式:可以在使用完成后,赋值null,释放引用空间
- for 循环中可以采用变量定义的方式,避免每次获取数组长度
const arr = [1,2,3,4,5]
for(let i = 0, len = arr.length; i < length; i++) {
console.log(i)
}
- 选择最优的循环结构: forEach > for > forin 可以采用文档碎片化节点添加的方式:
const fragEle = document.creatDocumentFragment()
for(var i = 0; i < 10; i++) {
var oP = document.creatElemnt('p')
oP.innerHTML = i
fragEle.appendChild(oP)
}
// 等待执行完成之后,在一次添加到body中
document.body.appendChild(fragEle)
- 如果是文中已有的节点,可以采用克隆的方式,更节省性能
for(let i = 0; i < 3; i++) {
let newP = oldP.cloneNode(false)
newP.innerHTML = i
document.body.appendChild(newP)
}
通过字面量替换 new Object
// let a1 = new Array(3)
// a1[0] = 1
// a2[1] = 2
// a3[2] = 3
let a = [1,2,3]