在前端快速发展的今天,如果不能时刻保持学习就会很快被淘汰。分享一下JavaScript 性能优化的相关实例,文章有点长,希望对大家有所帮助。每天进步一点点。
一、基础知识
关于JavaScript 性能优化的基础知识参见 JavaScript性能优化
二、代码优化实例1
使用工具精准测试 JavaScript 性能:工具1链接、工具2链接
本质上就是采集大量的执行样本进行数学统计和分析
PS:最近上面这两个工具经常大姨妈.
1、慎用全局变量
// 为什么要慎用
// 全局变量定义在全局执行上下文,是所有作用域链的顶端,导致变量查找消耗时间过长
// 全局执行上下文一直存在于上下文执行栈,知道程序退出,对于GC工作也是不利的
// 如果某个局部作用域出现了同名变量则会遮蔽或污染全局变量
// 使用全局变量
var i, str = ''
for (i = 0; i < 100; i++) {
str += i
}
// 使用局部变量
for (let i = 0; i < 100; i++) {
let str = ''
str += i
}
在工具2【JSBench.Me】中运行,通过对比很容看出使用局部变量执行效率更高
2、缓存全局变量
// 将使用中无法避免的全局变量缓存到局部
// 比如说在查找DOM元素的时候就不可避免的用到document
// 如果需要大量的查找元素
// 这个时候缓存document到局部就会提升我们的执行效率
// 假如存在大量的按钮
// 执行效率低
function getBtn () {
let btn1 = document.getElementById('btn1')
let btn2 = document.getElementById('btn2')
let btn3 = document.getElementById('btn3')
let btn4 = document.getElementById('btn4')
...
}
// 执行效率高
function getBtn () {
let doc = document
let btn1 = doc.getElementById('btn1')
let btn2 = doc.getElementById('btn2')
let btn3 = doc.getElementById('btn3')
let btn4 = doc.getElementById('btn4')
...
}
3、通过原型对象添加附加方法
// 在原型对象上新增实例对象需要的方法
// 执行效率低
var fn1 = function() {
this.foo = function() {
console.log(111)
}
}
let f1 = new fn1()
// 执行效率高
var fn2 = function() {}
fn2.prototype.foo = function() {
console.log(111)
}
let f2 = new fn2()
4、避开闭包陷阱
// 假设有一个id为btn的按钮
function foo() {
val el = document.getElementById('btn')
el.onclick = function() {
console.log(el.id)
}
el = null // 解决闭包产生的内存泄漏
}
foo()
5、避免属性访问方法使用
// JS不需属性访问方法,所有属性都是外部可见的
// 使用属性访问方法只会增加一层重定义,没有访问的控制力
// 执行效率低
function Person () {
this.name = 'zhagnsan'
this.age = 20
this.getAge = function () {
return this.age
}
}
const p1 = new Person()
const a = p1.getAge
// 执行效率高
function Person () {
this.name = 'zhagnsan'
this.age = 20
}
const p2 = new Person()
const b = p2.age
6、for循环优化
let arr = Array(10).fill('123')
// 执行效率低
for (let i = 0; i < arr.length; i++) {
console.log(i)
}
// 执行效率高
for (let i = 0, len = arr.length; i < len; i++) {
console.log(i)
}
7、选择最优循环方法
let arr = Array(10).fill('123')
// 执行效率最高,如果不做什么操作,可优选forEach循环
arr.forEach(item => {
console.log(item)
})
// 执行效率高
for (let i = 0, len = arr.length; i < len; i++) {
console.log(arr[i])
}
// 执行效率低
for (let i in arr) {
console.log(arr[i])
}
8、文档碎片优化节点添加
// 节点的添加操作必然会有回流和重绘
// 执行效率低
for (let i = 0; i < 10; i++) {
var oP = document.createElement('p')
oP.innerHtml = i
document.body.appendChild(oP)
}
// 执行效率高
const fragEle = document.createDocumentFragment()
for (let i = 0; i < 10; i++) {
var oP = document.createElement('p')
oP.innerHtml = i
fragEle.appendChild(oP)
}
document.body.appendChild(fragEle)
9、克隆优化节点操作
// 假如已经有一个p标签 <p id="p1">old</p>
// 执行效率低
for (let i = 0; i < 3; i++) {
var oP = document.createElement('p')
oP.innerHtml = i
document.body.appendChild(oP)
}
// 执行效率高
var oldP = document.getElementById('p1')
for (let i = 0; i < 3; i++) {
var newP = oldP.cloneNode(false)
newP.innerHtml = i
document.body.appendChild(newP)
}
10、直接量替换 new Object
// 执行效率高
var a = [1, 2, 3]
// 执行效率低
var a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3
三、代码优化实例2
1、堆栈中的JS执行过程
具体分析详解参见:一段代码详解JS在堆栈中的执行过程
2、减少判断层级
// 在编写代码的时候,很可能出现判断条件嵌套的场景
// 可以通过提前 return 掉那些不通过的条件,来优化执行效率
// 需求:
// 当前有一些视频学习课程,分为多个视频类型
// 每个视频类型下面又分为免费和收费模块
// 假设前5个小节是免费的,后面是收费的
// 执行效率低
function fn(part, chapter) {
const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']
if (part) {
if (parts.includes(part)) {
console.log('属于当前课程')
if (chapter > 5) {
console.log('需要付费观看')
}
}
} else {
console.log('请确认模块信息')
}
}
fn('ES2016', 6)
// 执行效率高
function fn(part, chapter) {
const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']
if (!part) {
console.log('请确认模块信息')
return
}
if (!parts.includes(part)) {
console.log('不属于当前课程')
return
}
console.log('属于当前课程')
if (chapter > 5) {
console.log('需要付费观看')
}
}
fn('ES2016', 6)
3、减少作用域链查找层级
// 执行效率低
var name = 'zhangsan'
function fn () {
name = 'lisi' // name 属于全局作用域,修改了全局作用域的 name 值
function fn2() {
var age = 20 // 属于当前fn2作用域
// 当前作用域存在age,直接输出age
console.log(age)
// 当前作用域不存在name,向上查找fn作用域,也不存在name,
// 再向上查找全局作用域找到name,输出name
console.log(name)
}
fn2()
}
fn()
// 执行效率高
var name = 'zhangsan'
function fn () {
var name = 'lisi' // name 属于当前fn作用域
function fn2() {
var age = 20 // 属于当前fn2作用域
// 当前作用域存在age,直接输出age
console.log(age)
// 当前作用域不存在name,向上查找fn作用域找到name,输出name
console.log(name)
}
fn2()
}
fn()
4、减少数据读取次数
// 假设存在这样一个DOM节点 <div id="skip" class="skip"></div>
// 执行效率低
var box = document.getElementById("skip")
function hasEle(ele, cls) {
// 这个地方ele是一个对象,所用到的属性可能很多,嵌套层级可能很深
// 可能在下面也会用到很多次,每次都会去读取数据
return ele.className == cls
}
console.log(hasEle(box, 'skip'))
// 执行效率高
var box = document.getElementById("skip")
function hasEle(ele, cls) {
// 将对象的属性缓存起来,下面使用的时候就不会多次去读取数据
const className = ele.className
return className == cls
}
console.log(hasEle(box, 'skip'))
5、字面量与构造式
// 对于引用类型
// 执行效率低
const test = () => {
// new Object() 可以看做在调用一个函数,做的事情更多一些
let obj = new Object()
obj.name = 'zhangsan'
obj.age = 20
obj.slogan = '好好学习,天天长胖'
return obj
}
console.log(test())
// 执行效率高
const test = () => {
// 使用字面量的方式,直接开辟空间存入数据
let obj = {
name: 'zhangsan',
age: 20,
slogan: '好好学习,天天长胖'
}
return obj
}
console.log(test())
// 对于基本数据类型
// 执行效率高
const str1 = '好好学习,天天长胖'
console.log(str1)
// 执行效率低
const str2 = new String('好好学习,天天长胖')
console.log(str2)
// 总结:字面量创建执行效率高,构造函数执行效率低
6、减少循环体中活动
// 执行效率低
const test = () => {
const arr = ['zhangsan', '20', '好好学习,天天长胖']
for(let i = 0; i < arr.length; i++) {
console.log(arr[i])
}
}
test()
// 执行效率高
const test = () => {
const arr = ['zhangsan', '20', '好好学习,天天长胖']
for(let i = 0, len = arr.length; i < len; i++) {
console.log(arr[i])
}
}
test()
// 执行效率最高
const test = () => {
const arr = ['zhangsan', '20', '好好学习,天天长胖']
let len = arr.length
while(len--) {
console.log(arr[len])
}
}
test()
7、减少声明及语句
// 假设有一个DOM节点 <div id="box" style="width: 100px; height: 100px;"></div>
// 执行效率低
var box = document.getElementById('box')
const test = () => {
const w = box.offsetWidth
const h = box.offsetHeight
return w * h
}
test()
// 执行效率高
var box = document.getElementById('box')
const test = () => {
return box.offsetWidth * box.offsetHeight
}
test()
// 可能会有人认为这个和 缓存变量 优化执行效率冲突
// 具体情况要具体分析:
// 如果变量要经常使用,那么缓存可以提高执行效率
// 如果变量只会使用一次或者很少的几次,那么减少声明则会提升效率
// 执行效率低
const test = () => {
const name = 'zhangsan'
const age = 20
const slogan = '好好学习,天天长胖'
return name + age + slogan
}
// 执行效率高
const test = () => {
const name = 'zhangsan', age = 20, slogan = '好好学习,天天长胖'
return name + age + slogan
}
// 减少声明语句数可以提升执行效率
8、采用事件委托
// 如果通过for循环,给每个子节点绑定点击事件
// 当子节点数量特别庞大的时候,就特别耗性能
// 这个时候使用事件委托的话,就可以只给父元素绑定点击事件,提升执行效率