JS性能优化

1.全局变量Vs局部变量

case:

// bad
var i
var s = ''
for (i = 0; i < 1000; i++) {
    s += i
}

// good
for (let i = 0; i < 1000; i++) {
    let s = ''
    s += i
}

测试结果:


image.png

结论:尽量使用let代替var关键字缩小作用域。

2.全局变量的缓存Vs不缓存

case:

// bad
function getBtn() {
      let oBtn1 = document.getElementById('btn1')
      let oBtn3 = document.getElementById('btn3')
      let oBtn5 = document.getElementById('btn5')
      let oBtn7 = document.getElementById('btn7')
      let oBtn9 = document.getElementById('btn9')
    }

// good
    function getBtn2() {
      let obj = document
      let oBtn1 = obj.getElementById('btn1')
      let oBtn3 = obj.getElementById('btn3')
      let oBtn5 = obj.getElementById('btn5')
      let oBtn7 = obj.getElementById('btn7')
      let oBtn9 = obj.getElementById('btn9')
    }

测试结果:


image.png

结论:局部缓存全局变量比不缓存的优势不是那么明显

3.方法添加到构造方法里Vs添加到原型链上

case:

// bad
let fn1 = function () {
    this.hello = function() {
        console.log('hello')
    }
}

let f1 = new fn1()


// good
let fn2 = function() {}
fn2.prototype.hello = function() {
    console.log('hello')
}
let f2 = new fn2()

测试结果:


image.png

结论:方法添加到原型链上比放到构造方法里,构造对象的速度更快
问题:如果f1,f2分别调用hello(),测试的结果却是相反的,为何?


image.png

4.直接获取属性值Vs调用属性getter方法

case:

// setup
class Person {
    name = undefined
    age = undefined
    getAge() {
        return this.age
    }
}

let p = new Person()
p.name = 'james'
p.age = 18

// bad
let n2 = p.getAge()

// good
let n1 = p.name

测试结果:


image.png

结论:直接获取属性值比调用getter方法性能上稍微好一些

5. 添加大量标签:直接添加Vs使用文档碎片分步添加

case:

    // bad
    for (var i = 0; i < 10; i++) {
      var oP = document.createElement('p')
      oP.innerHTML = i 
      document.body.appendChild(oP)
    }

    // good
    const fragEle = document.createDocumentFragment()
    for (var i = 0; i < 10; i++) {
      var oP = document.createElement('p')
      oP.innerHTML = i 
      fragEle.appendChild(oP)
    }
    document.body.appendChild(fragEle)

测试结果:


image.png

结论:添加大量标签时,先添加到createDocumentFragment,然后再添加到body上比直接添加到body上性能高。

6.添加已经页面上已经存在的相同属性的标签:重新生成Vs克隆

case:


  

old

测试结果:


image.png

结论:添加页面上已经存在并且相同属性的标签,应该使用克隆方式添加。

7.字面量创建Vs构造方法创建

字符串

case:

// bad
let s1 = new String('hello')

// good
let s2 = 'hello'

测试结果:


image.png
数字

case:

// bad
let n1 = new Number(10)

// good
let n2 = 10

测试结果:


image.png
布尔

case:

// bad
let b1 = new Boolean(true)

// good
let b2 = true

测试结果:


image.png
数组

case:

// bad
let a1 = new Array()
a1[0] = 1
a1[1] = 2
a1[2] = 3

// good
let a2 = [1, 2, 3]

测试结果:


image.png
Map

case:

// bad
let m1 = new Map()
m1['k1'] = 'v1'
m1['k2'] = 'v2'

// good
let m2 = {'k1': 'v1', 'k2': 'v2'}

测试结果:


image.png

对象

case:

// bad
let o1 = new Object()
o1.name = 'james'
o1.age = 18

// good
let o2 = {name: 'james', age: 18}

测试结果:


image.png

结论:

  1. 基本类型:使用字面量创建比使用构造方法创建性能上要快95%以上,因为构造方法创建出来的是对象,是引用类型,而基本类型是值类型,他们在底层的实现和存储上的差别是很大的,所以表现出的效率差距也很大。
  2. 引用类型:使用字面量创建比使用构造方法创建依然有小幅优势。

8.循环方式的比较

由于使用JSBench测试的时候情况很不稳定,所以此处用代码测试时间。
因为for in 在数据量很大的情况下非常慢,case中已忽略for in的比较,因为它的效率是最低的。
case:

var a1 = new Array(1000000).fill(1)
var iteranum = 1000

// 遍历
// 1.常规for循环
console.time('for')
for(let k = 0; k < iteranum; k++) {
    for (let i = 0; i < a1.length; i++) {
        //console.log(a1[i])
        let j = a1[i] + 1
    }
}
console.timeEnd('for')

// 2.常规for循环(优化)
console.time('for2')
for(let k = 0; k < iteranum; k++) {
    for (let i = 0, l = a1.length; i < l; i++) {
        //console.log(a1[i])
        let j = a1[i] + 1
    }
}
console.timeEnd('for2')

// 3.for of
console.time('for of')
for(let k = 0; k < iteranum; k++) {
    for (let v of a1) {
        //console.log(v)
        let j = v + 1
    }
}
console.timeEnd('for of')


// // 4.for in
// console.time('for in')
// for(let k = 0; k < iteranum; k++) {
//     for (let v in a1) {
//         //console.log(a1[v])
//         let j = a1[v] + 1
//     }
// }
// console.timeEnd('for in')

// 5.forEach
console.time('forEach')
for(let k = 0;k < iteranum; k++) {
    a1.forEach(function(e) {
        //console.log(e)
        let j = e + 1
    })
}
console.timeEnd('forEach')

测试机器:
CPU: 3.1 GHz 双核Intel Core i5
内存:8 GB 2133 MHz LPDDR3

测试结果(多次运行,各数据浮动不大):

for: 792.904ms
for2: 693.385ms
for of: 1831.124ms
forEach: 13303.218ms

结论:
在循环次数很大的情况下,遍历效率
for(缓存长度) > for > for of > forEach > for in

你可能感兴趣的:(JS性能优化)