代码优化
如何精准得测试js性能
采集大量得执行样本进行数学统计和分析
1. Benchmark
npm i benchmark // 越大性能越好(Ops/sec)
const Benchmark = require('benchmark');
const suite = new Benchmark.Suite;
suite
.add('RegExp#test', function () {
// 这里放入对比代码
})
.add('String#indexOf', function () {
// 这里放入对比代码
})
// add listeners
.on('cycle', function (event) {
console.log(String(event.target));
})
.on('complete', function () {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
// run async
.run({ 'async': true });
2. jsBench.me
js代码执行效率的一个网站
优化方法
1. 慎用全局变量
.add('RegExp#test', function () {
var i, str = ''
for (i = 0; i < 1000; i++) {
str += i
}
})
.add('String#indexOf', function () {
for (let i = 0; i < 1000; i++) {
let str = ''
str += i
}
})
2. 缓存全局变量
将使用中无法避免的大量需要重复的全局变量缓存到局部(document)
.add('RegExp#test', function () {
let oBtn1 = document.getElementById('btn1')
let oBtn1 = document.getElementById('btn3')
let oBtn1 = document.getElementById('btn5')
let oBtn1 = document.getElementById('btn7')
let oBtn1 = document.getElementById('btn9')
})
.add('String#indexOf', function () {
let obj = document
let oBtn1 = obj.getElementById('btn1')
let oBtn1 = obj.getElementById('btn3')
let oBtn1 = obj.getElementById('btn5')
let oBtn1 = obj.getElementById('btn7')
let oBtn1 = obj.getElementById('btn9')
})
3. 在原型对象上新增实例对象需要的方法
.add('RegExp#test', function () {
var fn1 = function () {
this.foo = function () {
console.log(111);
}
}
let f1 = new fn1()
})
.add('String#indexOf', function () {
var fn2 = function () {
fn2.prototype.foo = function () {
console.log(111);
}
}
let f2 = new fn2()
})
4. 避开闭包的陷阱
使用不当很容易出现内存泄漏
不要为了闭包而闭包
function foo(){
var el = document.getElementById('btn')
el.onclick = function(){
console.log(el.id);
}
el = null // 当浏览器的document对象被引用2次,且该对象被删除的时候会照成内存泄漏
}
5. 避免属性访问方法使用
js不需属性的访问方法,所有属性都是外部可见的
使用属性方法只会增加一层重定义,没有访问的控制力
.add('RegExp#test', function () {
function Person(){
this.name = 'icoder'
this.getAge = function(){
return this.age
}
}
const p1 = new Person()
const a = p1.getAge()
})
.add('String#indexOf', function () {
function Person(){
this.name = 'icoder'
this.age = 18
}
const p2 = new Person()
const b = p2.age
})
6. For循环优化
.add('RegExp#test', function () {
let arr = [1, 2, 3, 4, 5]
for (var i = 0; i < arr.length; i++) {
console.log(i);
}
})
.add('String#indexOf', function () {
let arr = [1, 2, 3, 4, 5]
for (var i = 0, len = arr.length; i < len; i++) { // 少了每次都需要重新计算长度
console.log(i);
}
})
7. 选择最优的循环方法
8. 文档碎片优化节点的添加
.add('RegExp#test', function () {
for (let i = 0; i < 10; i++) {
var oP = document.createElement('p')
oP.innerHTML = i
document.body.appendChild(oP)
}
})
.add('String#indexOf', function () {
const frageEle = document.createDocumentFragment()
for (let i = 0; i < 10; i++) {
var oP = document.createElement('p')
oP.innerHTML = i
frageEle.appendChild(oP)
}
document.body.appendChild(frageEle)
})
9. 克隆优化节点操作
.add('RegExp#test', function () {
for (let i = 0; i < 3; i++) {
let oP = document.createElement('p')
oP.innerHTML = i
document.body.appendChild(oP)
}
})
.add('String#indexOf', function () {
var oldP = document.getElementById('box1')
for (let i = 0; i < 3; i++) {
let newP = oldP.cloneNode(false)
oP.innerHTML = i
document.body.appendChild(newP)
}
})
10. 直接量替换 new Objec
.add('RegExp#test', function () {
let a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3
})
.add('String#indexOf', function () {
const a = [1, 2, 3]
})
11. 减少判断层级
// function doSomething(part, chapter) {
// const parts = ['ES2016', '工程化', 'Vue', 'React']
// if (part) {
// if (parts.includes(part)) {
// console.log('属于当前课程');
// if (chapter > 5) {
// console.log('您需要提供vip身份');
// }
// }
// } else {
// console.log('请确认模块信息');
// }
// }
function doSomething(part, chapter) {
const parts = ['ES2016', '工程化', 'Vue', 'React']
if (!part) {
console.log('请确认模块信息');
return
}
if (!parts.includes(part)) return
console.log('属于当前课程');
if (chapter > 5) {
console.log('您需要提供vip身份');
}
}
12. 减少作用域链查找层级
缺点:消耗内存空间;优点:因为向上查找的作用域层级更近了,查找速度更快
// let name = 'xh'
// function foo() {
// name = 'cx'
// function baz() {
// var age = 38
// console.log(age);
// console.log(name);
// }
// baz()
// }
let name = 'xh'
function foo() {
let name = 'cx'
function baz() {
var age = 38
console.log(age);
console.log(name);
}
baz()
}
13. 减少数据读取次数
数据表现形式
字面量,局部变量,数组元素,对象属性
字面量,局部变量是最快的,因为他们都可以放在栈区当中
而数组元素,对象属性会慢一些,因为对象上往往会考虑到原型链的查找,和作用域链的道理一样
减少嵌套层级或深度(缺点是内存换时间),对象数据提前缓存,后续直接使用
// var oBox = document.getElementById('skip')
// function hasEle(ele,cls){
// return ele.className == cls
// }
// console.log(hasEle(oBox,'skip'));
var oBox = document.getElementById('skip')
function hasEle(ele,cls){
let clsname = ele.className
return clsname == cls
}
console.log(hasEle(oBox,'skip'));
14. 字面量与构造式
// let test = () => {
// let obj = new Object()
// obj.name = 'cx'
// return obj
// }
let test = () => {
let obj = {
name:'cx'
}
return obj
}
console.log(test());
// let str1 = 'cx'
let str1 = new String('cx')
console.log(str1);
15. 减少循环体中活动
// let test = () => {
// let arr = ['cx', 'hello']
// for (i = 0; i < arr.length; i++) {
// console.log(document.getElementById('a'));
// console.log(arr[i]);
// }
// }
let test = () => {
let arr = ['cx', 'hello']
let doc = document.getElementById('a')
for (i = 0, len = arr.length; i < len; i++) {
console.log(doc);
console.log(arr[i]);
}
}
16. 减少声明及语句数
代码执行前有个编译的过程(短),词法分析遇到关键字要做拆分-语法分析-生成语法树-再去转代码-并执行
// let test = (ele) => {
// let w = ele.offsetWidth
// let h = ele.offsetHight
// return w * h
// }
let test = (ele) => {
return ele.offsetWidth * ele.offsetHight
}
console.log(test(document.getElementById('a')));
// const test = () => {
// let name = 'cx'
// let slogan = 'hello'
// return name + slogan
// }
const test = () => {
let name = 'cx',
slogan = 'hello'
return name + slogan
}
console.log(test())
17. 采用事件绑定(委托)
// let list = document.querySelectorAll('li')
// function showTxt(e) {
// console.log(e.target.innerHTML);
// }
// for(let item of list){
// item.onclick = showTxt
// }
let ul = document.getElementById('ul')
ul.addEventListener('click', showTxt, true)
function showTxt(e) {
let obj = e.target
if (obj.nodeName.toLowerCase() === 'li') {
console.log(obj.innerHTML);
}
}