JS 代码怎么声明、循环怎么用才最快

3,063 阅读2分钟

前言

这些都是本人在网上搜集、平时常用的而且通过JSBench测试过的,如果读者们还有那些性能优化,欢迎在评论区中留言

内存问题外在表现

  • 页面出现延迟加载或经常性暂停
  • 页面持续性出现糟糕的性能
  • 页面的性能随时间延长越来越差

界定内存问题的标准

  • 内存泄漏:内存使用持续升高
  • 内存膨胀:在多数设备上都存在性能问题
  • 频繁垃圾回收:通过内存变化图进行分析

分离 DOM

  • 界面元素存活在 DOM 树上
  • 垃圾对象时的 DOM 节点
  • 分离状态的 DOM 节点

TimeLine记录内存 - Performance面板

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <button id="btn">新增</button>

    <script>
        const arr = []

        function test () {
          for (let i = 0; i < 1000000; i++) {
            document.body.appendChild(document.createElement('p'))
          }
          arr.push(new Array(1000000).join('x'))
        }

        document.getElementById('btn').addEventListener('click', test)
    </script>
</body>
</html>

堆块照监控内存 - Memory面板

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <button id="btn">新增</button>

    <script>
        const arr = []

        function test () {
          for (let i = 0; i < 1000000; i++) {
            document.body.appendChild(document.createElement('p'))
          }
          arr.push(new Array(1000000).join('x'))
        }

        document.getElementById('btn').addEventListener('click', test)
    </script>
</body>
</html>

如何确定频繁的垃圾回收

  • Timeline 中频繁的上升下降
  • 任务管理器中数据频繁的增加减小

性能优化

慎用全局变量

  • 全局变量定义在全局执行上下文,是所有作用域链的顶端
  • 全局执行上下文一直存在于上下文执行栈,直到程序退出
  • 如果某个局部作用域出现了同名变量则会遮蔽或污染全局
// 全局变量
var i, str = ''
for (i =0; i< 1000; i++) {
	str += i
}

// 局部变量(更快)
for (let i =0; i< 1000; i++) {
	let str = ''
	str += i
}

缓存全局变量

  • 将使用中无法避免的全局变量缓存到局部
// 非缓存方式
const obj = {
	name: 'jcode',
  age: 18
}

function fn () {
	console.log(obj.name)
  console.log(obj.name)
  console.log(obj.name)
  console.log(obj.name)
  console.log(obj.name)
}

fn()


// 缓存方式(更快)
const obj = {
	name: 'jcode',
  age: 18
}

function fn () {
	const o = obj
  console.log(o.name)
  console.log(o.name)
  console.log(o.name)
  console.log(o.name)
}

fn()

通过原型新增方法

  • 在原型对象上新增实例对象需要的方法
// 原型实例上定义
function Person () {
	this.sayHi = function () {
  	console.log('sayHi')
  }
}

const p1 = new Person()


// 原型上定义(更快)
function Person () {}
Person.prototype.sayHi = function () {
	console.log('sayHi')
}

const p1 = new Person()

避开闭包陷阱

  • 每次闭包调用都尽可能的销毁无用的变量
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<button id="btn" class="dd">add</button>

<script>
    function foo () {
        var el = document.getElementById('btn')
        el.onclick = function () {
            console.log(el.id, el.className)
        }
        el = null

    }

    foo()
</script>
</body>
</html>

避免属性访问方法使用

  • JavaScript 中的面向对象
    • JS 不需属性的访问方法,所有属性都是外部可见的
    • 使用属性访问方法只会增加一层重定义,没有访问的控制力
// 通过定义方法,间接访问属性(慢)
function Person () {
  this.name = 'jcode'
  this.age = 18
  this.getAge = function () {
    return this.age
  }
}

const p1 = new Person()
const a = p1.getAge()


// 直接访问属性(更快)
function Person () {
  this.name = 'jcode'
  this.age = 18
}
const p1 = new Person()
const b = p1.age

for 循环优化

var arr = [1, 2, 3, 4, 5, 6, 7]
for (var i = 0; i < arr.length; i++) {
	console.log(i)
}


// 更快
var arr = [1, 2, 3, 4, 5, 6, 7]
for (var i = 0, len = arr.length; i < len; i++) {
	console.log(i)
}

节点添加优化

  • 节点的添加操作必然会有回流和重绘
for (var i = 0; i < 10; i++) {
	var oP = document.createElement('p')
  oP.innerHTML = i
  document.body.appendChild(oP)
}


// 文档碎片方式添加(更快)
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)

克隆优化节点操作

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<p id="box1">old</p>

<script>
    for (var i =0; i < 5; i++) {
        var oP = document.createElement('p')
        oP.innerHTML = i
        document.body.appendChild(oP)
    }


    // 克隆的方式(更快) 
    var oldP = document.getElementById('box1')
    for (var i =0; i < 5; i++) {
        var newP = document.cloneNode(false)
        newP.innerHTML = i
        document.body.appendChild(newP)
    }
</script>
</body>
</html>

字面量替换 new Array

var arr = newArray(3)
arr[0] = 1
arr[1] = 2
arr[2] = 3

// 更快
var arr = [1, 2, 3]

减少判断层级

  • 尽量提前 return 来结束判断语句
  • 判断条件较多时,用 switch 来代替,性能更优

减少作用域链查找层级

减少数据读取次数

function (ele, cla) {
	return ele.className === cls
}


// 进行一次缓存,更快
function (ele, cla) {
  let clsName = ele.className
	return clsName === cls
}

字面量与构造式 - 字面量快

  • 引用类型相差不多
  • 基础数据类型相差很多
const obj = new Object()
obj.name = 'jcode'


// 字面量方式更快
const obj = {
  name: 'jcode'
}

减少循环体活动

  • 尽量将不变的操作提升到循环体之外
// 比正常的for循环快
const test = () => {
	var i
  var arr = [1, 2, 3]
  var len = arr.length
  for (i = 0; i < len; i++) {
    console.log(arr[i])
  }
}

// 更快
const test = () => {
  var arr = [1, 2, 3]
  var len = arr.length
  while (len--) {
    console.log(arr[len])
  }
}

减少声明及语句数

var name = 'jcode'
var age = 19


// 更快
var name = 'jcode',
    age = 19

采用事件绑定

采用事件委托

  <ul id="ul">
    <li>jcode</li>
    <li>18</li>
  </ul>
<script>
  var list = document.querySelectorAll('li')
  function showTxt (ev) {
    console.log(ev.target.innerHTML)
  }
  for(let item of list) {
    item.onclick = showTxt
  }
  // 下面的效率会更高一些 
  var ul = document.getElementById('ul')
  ul.addEventListener('click', showTxt, true)
  function showTxt (ev) {
    var obj = ev.target
    if (obj.nodeName.toLowerCase() === 'li') {
      console.log(obj.innerHTML)
    }
  }
</script>

参考文献:

语雀:www.yuque.com/xixieguibug…