Javascript 性能优化 1

176 阅读2分钟

内存管理

内存为什么需要管理?

function fn() { arrList = []
arrList[100000] = 'lg is a coder' }
fn()

image.png 比如在执行这段代码时,就会导致内存占用一直上升,内存泄漏的问题

  1. 申请空间 let obj = {}
  2. 使用空间 obj.name = 'lg'
  3. 释放空间 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]