前言
这些都是本人在网上搜集、平时常用的而且通过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 循环最优排序
- forEach(最快) > for(优化后的for) > for in
节点添加优化
- 节点的添加操作必然会有回流和重绘
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>
复制代码