Part2 · 前端工程化实战
JavaScript性能优化-工具及代码优化
文章说明:本专栏内容为本人参加【拉钩大前端高新训练营】的学习笔记以及思考总结,学徒之心,仅为分享。如若有误,请在评论区支出,如果您觉得专栏内容还不错,请点赞、关注、评论。共同进步!
本篇主要内容是JavaScript的性能优化,包含Performance工具及JavaScript代码优化
超长文、多图预警!!!超长文、多图预警!!!
一、Performance工具
1.Performance工具介绍
使用Chrome DevTools的performance面板可以记录和分析页面在运行时的所有活动。
为什么我们需要使用Performance工具,其原因有以下几点:
- GC的目的是为了实现内存空间的良性循环
- 良性循环的基石是合理使用
- 时刻关注才能确定是否合理
- 由于ECMAScript中未向开发者提供操作内存空间的API
- Performance工具提供了多种监控方式
Performance使用步骤为:
- 打开浏览器输入目标地址
- 进入开发人员工具面板(F12),选择performance
- 开启录制功能,访问具体界面
- 执行用户行为,一段时间后停止录制
- 分析界面中记录的内存信息
2.内存问题的体现
内存问题的体现分为外在表现和内在表现。
内存问题的外在表现:
- 页面出现延迟加载或经常性暂停
- 页面持续性出现糟糕的性能
- 页面的性能随时间延长越来越差
内存问题的内在表现:
- 内存泄漏:内存使用持续升高
- 内存膨胀:在多数设备上都存在性能问题
- 频繁垃圾回收:通过内存变化图进行分析
3.监控内存的几种方式
3.1任务管理器监控内存
打开浏览器,按键【Shift】+ 【Esc】,调出浏览器任务管理器。找到我们的目标标签页,刚开始可能没有JavaScript内存,可以在目标标签页任务上右键,然后选择JavaScript内存。
记录JavaScript内存(JavaScript堆占用的内存,表示界面中所有可达对象占用的内存)及内存占用空间(原生内存,DOM节点占用的内存),点击按钮,记录每次内存的变化。
3.2Timeline记录内存
上述浏览器任务管理器更多的是用于判断当前脚本是否存在内存问题,而不能具体定位到问题。我们使用Timeline时间线记录内存变化,更精确的记录到内存变化。
3.3堆快照查找分离DOM
什么是分离DOM
- 界面元素存货在DOM树上
- 垃圾对象时的DOM节点(当前DOM从存活的DOM树上分离,且js中没有应用这个DOM)
- 分离状态的DOM节点(当前DOM节点从当前DOM树分离,但js中还在应用它)
在点击按钮后,DOM中生成了分离的DOM,造成内存空间的浪费,因此我们需要将代码中的temEle置空,这样让GC对垃圾进行回收即可。
3.4判断是否存在频繁GC
为什么要确定频繁垃圾回收
- GC工作时应用程序是停止的
- 频繁且过长的GC会导致应用假死
- 用户使用中感知应用卡顿
确定频繁的垃圾回收:
- Timeline中频繁的上升下降
- 任务管理器中的数据频繁增加减小
二、代码优化
1.代码优化介绍
如何精准测试JavaScript性能:
- 本质上就是采集大量的执行脚本进行数学统计和分析
- 使用基于Benchmark.js的https://jsbench.me/使用
代码需要优化的原因:
- JavaScript中的内存管理自动完成
- 执行引擎会使用不同的GC算法
- 算法工作的目的是为了实现内存空间良性循环
- Performance工具检测内存变化
- JavaScript是单线程机制的解释性语言
2.慎用全局变量及缓存全局变量
全局变量的特点:
- 全局变量挂载在window下
- 全局变量至少有一个引用计数
- 全局变量存货更久,但持续占用内存
全局查找相关:
- 目标变量不存在于当前作用域内,通过作用域链向上查找
- 减少全局查找降低的时间消耗
- 减少不必要的全局变量定义
- 全局变量数据局部化
慎用全局变量:
- 全局变量定义在全局执行上下文,是否有作用域链的顶端
- 全局执行上下文一直存在于上下文执行栈,指导程序退出
- 如果某个局部作用于初夏了同名的变量则会遮蔽或午饭全局
慎用全局变量代码演示:
function fn() {
name = 'lg'
console.log(`${name} is a coder`)
}
fn()
function fn() {
const name = 'lg'
console.log(`${name} is a coder`)
}
fn()
测试结果:
var i, str = ''
for (i = 0; i < 1000; i++) {
str += i
}
for (let i = 0; i < 1000; i++) {
let str = ''
str += i
}
缓存全局变量代码演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>缓存全局变量</title>
</head>
<body>
<input type="button" value="btn" id="btn1">
<input type="button" value="btn" id="btn2">
<input type="button" value="btn" id="btn3">
<input type="button" value="btn" id="btn4">
<p>1111</p>
<input type="button" value="btn" id="btn5">
<input type="button" value="btn" id="btn6">
<p>222</p>
<input type="button" value="btn" id="btn7">
<input type="button" value="btn" id="btn8">
<p>333</p>
<input type="button" value="btn" id="btn9">
<input type="button" value="btn" id="btn10">
<script>
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')
}
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')
}
</script>
</body>
</html>
测试结果:
3.通过原型对象添加附加方法
代码演示:
var fn1 = function() {
this.foo = function() {
console.log(11111)
}
}
let f1 = new fn1()
var fn2 = function() {}
fn2.prototype.foo = function() {
console.log(11111)
}
let f2 = new fn2()
测试结果:
4.避开闭包陷阱
关于闭包:
- 闭包是一种强大的语法
- 闭包使用不当很容易出现内存泄漏
- 不要为了闭包而闭包
代码示例:
function test(func) {
console.log(func())
}
function test2() {
var name = 'lg'
return name
}
test(function() {
var name = 'lg'
return name
})
test(test2)
5.避免属性访问方法使用
- JavaScript不需要属性的访问方法,所有属性都是外部可见的
- 使用属性访问方法只会增加一层重定义,没有访问的控制力
代码示例:
function Person() {
this.name = 'icoder'
this.age = 18
this.getAge = function() {
return this.age
}
}
const p1 = new Person()
const a = p1.getAge()
function Person() {
this.name = 'icoder'
this.age = 18
}
const p2 = new Person()
const b = p2.age
测试结果:
6.For循环优化及选择最优循环方法
代码示例:
var arrList = []
arrList[10000] = 'icoder'
for (var i = 0; i < arrList.length; i++) {
console.log(arrList[i])
}
for (var i = arrList.length; i; i--) {
console.log(arrList[i])
}
测试结果:
选择最优循环方法
代码示例:
var arrList = new Array(1, 2, 3, 4, 5)
arrList.forEach(function(item) {
console.log(item)
})
for (var i = arrList.length; i; i--) {
console.log(arrList[i])
}
for (var i in arrList) {
console.log(arrList[i])
}
测试结果(forEach效率最高):
7.文档碎片优化节点添加、克隆优化节点操作
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>优化节点添加</title>
</head>
<body>
<script>
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)
</script>
</body>
</html>
测试结果:
克隆优化节点操作
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>克隆优化节点操作</title>
</head>
<body>
<p id="box1">old</p>
<script>
for (var i = 0; i < 3; i++) {
var oP = document.createElement('p')
oP.innerHTML = i
document.body.appendChild(oP)
}
var oldP = document.getElementById('box1')
for (var i = 0; i < 3; i++) {
var newP = oldP.cloneNode(false)
newP.innerHTML = i
document.body.appendChild(newP)
}
</script>
</body>
</html>
测试结果:
8.直接量替换new Object
代码示例:
var a = [1, 2, 3]
var a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3
测试结果:
9.堆栈中的JS执行过程
let a = 10;
function foo(b) {
let a = 2;
function baz(c) {
console.log(a+b+c);
}
return baz
}
let fn = foo(2);
fn(3);
10.减少判断层
function doSomething(part, chapter) {
const parts = ['ES2015', '工程化', 'Vue', 'Reach', 'Node'];
if (part) {
if (parts.includes(part)) {
console.log('属于当前课程')
if (chapter > 5) {
console.log('您需要提供VIP身份')
}
}
} else {
console.log('请确认模块信息')
}
}
doSomething('ES2015', 6)
function doSomething2(part, chapter) {
const parts = ['ES2015', '工程化', 'Vue', 'Reach', 'Node'];
if (!part) {
console.log('确认模块信息')
return
}
if (!parts.includes(part)) return;
console.log('属于当前课程')
if (chapter > 5) {
console.log('您需要提供VIP身份')
}
}
doSomething2('ES2015', 6)
11.减少作用域链查找层级
代码示例:
var name = 'zce';
function foo() {
name = 'zce666' // 这里的name是全局的
function baz() {
var age = 28
console.log(age)
console.log(name)
}
baz()
}
foo()
var name = 'zce';
function foo() {
var name = 'zce666' // 这里的name是全局的
function baz() {
var age = 28
console.log(age)
console.log(name)
}
baz()
}
foo()
测试结果:
12.减少数据读取次数
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>减少数据读取次数</title>
</head>
<body>
<div id="skip" class="skip"></div>
<script>
var oBox = document.getElementById('skip')
function hasEle(ele, cls) {
return ele.className === cls
}
function hasEle(ele, cls) {
var className = ele.className
return className === cls
}
console.log(hasEle(oBox, 'skip'))
</script>
</body>
</html>
测试结果:
13.字面量与构造式
代码示例:
var test = () => {
let obj = new Object();
obj.name = 'zce';
obj.age = '28';
obj.slogan = '我为前端而活'
return obj
}
var test = () => {
let obj = {
name: 'zce',
age : '28',
slogan: '喔喔前端而活'
}
return obj
}
console.log(test())
测试结果:
14.减少循环体中活动
代码示例:
var test = () => {
var i
var arr = ['zce', 28, '我为前端而活']
for (let i = 0; i < arr.length; i++) {
console.log(arr[i])
}
}
var test = () => {
var i
var arr = ['zce', 28, '我为前端而活']
var len = arr.length
for (let i = 0; i < len; i++) {
console.log(arr[i])
}
}
测试结果:
15.惰性函数与性能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="btn"></button>
<script>
var oBtn = document.getElementById('btn');
function foo() {
console.log(this)
}
function addEvent(obj, type, fn) {
if (obj.addEventListener()) {
obj.addEventListener(type, fn, false)
} else if (obj.attachEvent) {
obj.attachEvent('on'+type, fn)
} else {
obj['on' + type] = fn
}
}
function addEvent(obj, type, fn) {
if (obj.addEventListener()) {
addEvent = obj.addEventListener(type, fn, false)
} else if (obj.attachEvent) {
addEvent = obj.attachEvent('on'+type, fn)
} else {
addEvent = obj['on' + type] = fn
}
return addEvent
}
</script>
</body>
</html>
16.减少声明及语句数
代码示例:
var test = () => {
let w = 200
let h = 300
return w * h
}
var test = () => {
return 200 * 300
}
17.采用事件绑定
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="ul">
<li>Leo</li>
<li>28</li>
<li>我为前端而活</li>
</ul>
<script>
var list = document.querySelectorAll('li')
function showText(ev) {
console.log(ev.target.innerHTML)
}
for (let listElement of list) {
item.onclick = showText
}
var oUl = document.getElementById('ul')
oUl.addEventListener('click', showText, true)
</script>
</body>
</html>
今日分享就到了这里,上面很多的概念性问题,要完全的理解并使用这些新的知识,需要很长一段时间。多用、多查、多做!
Part1 JavaScript部分分享完成,后面进行前端工程化的分享,共同进步!记录:2020/11/16