JavaScript 性能优化实例

在前端快速发展的今天,如果不能时刻保持学习就会很快被淘汰。分享一下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】中运行,通过对比很容看出使用局部变量执行效率更高

JavaScript 性能优化

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循环,给每个子节点绑定点击事件
// 当子节点数量特别庞大的时候,就特别耗性能
// 这个时候使用事件委托的话,就可以只给父元素绑定点击事件,提升执行效率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值