文章说明:本文章为拉钩大前端训练营所做笔记和心得,若有不当之处,还望各位指出与教导,谢谢 !
一、JSBench使用
相对于JSperf,JSBench(https://jsbench.me/)综合性能更强.
Teardown:做一个收尾的工作,比如我们连接完数据库之后,如果操作完了数据,我们就应该把这个连接释放掉。正常来讲,我们js代码执行完之后,可能我们调用的函数操作都是一样的,没必要写在用例里面,所以把他们抽离出来放到Teardown里面用就可以了。
测试的时候尽量只开一个标签页。
二、堆栈中的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)
js代码在开始执行之后,首先会在堆内存里创建执行环境栈,然后我们用它去存放了不同的执行上下文。代码从上往下去执行,我们最先创建的应该是ECG也就是全局上下文,这里面,我们把全局作用域下面一些代码进行申明和存放,再有就是基本数据类型值它是直接存放在我们栈内存里边,而对于引用类型来说,他是存放在我们的堆区里面,一般是由我们的GC来去进行回收的处理,而栈区里面的东西,出栈时肯定是由当前js的主线程去管理,每当我们遇到函数执行的时候,它就会去重新生成一个执行上下文,然后进栈。代码执行完成以后呢,由是否产生闭包来决定我们当前的上下文它里面所引用的堆到底要不要去被释放掉。
三、减少判断层级
function doSomething(part,chapter){
const parts = ['ES2016','工程化','Vue','React','Node']
if(part){
if(parts.includes(part)){
console.log('属于当前课程')
if(chapter >5){
console.log('您需要提供 VIP 身份')
}
}
} else {
console.log('请确认模块信息')
}
}
// doSomething('ES2016',6)
function doSomething(part,chapter){
const parts = ['ES2016','工程化','Vue','React','Node']
if(!part){
console.log('请确认模块信息')
return
}
if (!parts.includes(part)) return
if(chapter > 5){
console.log('您需要提供 VIP 身份')
}
}
doSomething('ES2016',6)
四、减少作用域链查找层级
// var name = 'zce'
// function foo(){
// name = 'zce666' //这里的Name是属于全局的
// function baz(){
// var age = 38
// console.log(age)
// console.log(name)
// }
// baz()
// }
// foo()
var name = 'zce'
function foo(){
var name = 'zce666' //这里的Name是属于全局的,当前面加var时,此时的name就不用了再往外查找了
function baz(){
var age = 38
console.log(age)
console.log(name)
}
baz()
}
foo()
五、减少数据读取次数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
<title>减少数据读取次数</title>
</head>
<body>
<div id="skip" class="skip"></div>
<script type="text/javascript">
var oBox = document.getElementById('skip')
// function hasEle(ele,cls){
/*若是按照ele.className这样写的话,相当于每次使用数据的时候都要去查询一下,如果这个查询的属性在我们当前对象里面又进行了嵌套,
层级非常的深,这时候所消耗的时间就非常的多,所以考虑把ele.className缓存起来*/
// return ele.className === cls
// }
function hasEle(ele,cls){
var clsname = ele.className//只需读取一次缓存起来
return clsname === cls
}
console.log(hasEle(oBox,'skip'))
</script>
</body>
</html>
这样的快还是建立在对空间消耗的前提下的 ,如果是个引用类型那就是一个比较大的空间了。若需要读取多次,则尽可能的缓存起来
六、字面量与构造式
let test = () =>{// 把创建对象步骤放到函数里面,用new关键字构造函数的方式来创建obj
let obj = new Object()
obj.name = 'zce'
obj.age = 38
obj.slogan = '我爱你'
return obj
}//做这个操作的时候好比调用一个函数,涉及到了函数的调用,所以它事情就做的更多一些,时间就消耗的多一些,扩容的时候这种方法更好一些
let test = () =>{
let obj = {
name:'zce',
age:38,
slogan:'我为前端而活'
}
return obj
}//而这个就相当于开辟一个空间往里面存东西就行了
console.log(test())
//==============================================================================================================
var str1 = 'zce说我为前端而活'
var str2 = new String('zce说我为前端而活')/*这是一个对象,它的好处是可以直接沿着原型链调用方法,但是上面的那个不能够调用,对于我们还是少占用空间,
这样子创建的话肯定要有多余的空间来存放obj上的属性或者方法*/
str1.slice //这样操作时会默认先把str1转成str2这种对象,然后再调用方法,即使不转也是属于我们当前对象下的一个实例,按照原型链也能找到
console.log(str1)
console.log(str2)
七、 减少循环体中活动
// var test = () =>{
// var i
// var arr = ['zce',38,'我为前端而活']
// for(i = 0;i<arr.length;i++){
// console.log(arr[i])
// }
// }
// var test = () =>{
// var i
// var arr = ['zce',38,'我为前端而活']
// var len = arr.length
// for(i = 0;i<len;i++){//尽可能的减少循环体里面的操作
// console.log(arr[i])
// }
// }
var test = () =>{
var arr = ['zce',38,'我为前端而活']
var len = arr.length
while(len --){//从后往前找,当小于0时就不成立了
console.log(arr[len])
}
}
test()
八、减少声明及语句数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
<title>减少声明及语句数</title>
</head>
<body>
<div id="box" style="width: 100px;height: 100px;"></div>
<script type="text/javascript">
// var oBox = document.getElementById('box')
// var test = (ele) => {
// let w = ele.offsetWidth
// let h = ele.offsetHeight
// return w * h
// }
// var test = (ele) => {
// return ele.offsetWidth * ele.offsetHeight
// }
// console.log(text(oBox))
var test = () => {// 更倾向于这样写,代码结构清晰一些,有利于维护
var name = 'zce'
var age = 38
var slogan = '我为前端而活'
return name + age + slogan
}
var test = () => {
var name = 'zce',
age = 38,
slogan = '我为前端而活'
return name + age + slogan
}
console.log(test())
</script>
</body>
</html>
九、惰性函数与性能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
<title>惰性函数与性能</title>
</head>
<body>
<button id="btn">
点击
</button>
<script type="text/javascript">
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
}
addEvent(oBtn,'click',foo)
</script>
</body>
</html>
基于当前的场景下,我们看到的结果的确是 下面这种写法执行速度更慢一些而已。这种做法其实主要就是对addEvent的值做一个重新赋值操作,当我们的条件满足之后,第二次进来就不需要判断,而直接进来把它上一次成功的值去调用就可以了。具体来说我们该如何选择呢,取决于我们到底想要上面那种清晰明了的直观的类型的代码,还是下面那种。
十、采用事件委托
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
<title>采用事件委托</title>
</head>
<body>
<ul id="ul">
<li>zce</li>
<li>28</li>
<li>我为前端而活</li>
</ul>
<script type="text/javascript">
var list = document.querySelectorAll('li')
// function showTxt(ev){
// console.log(ev.target.innerHTML)
// }
// for(let item of list){
// item.onclick = showTxt
// }
var oUl = document.getElementById('ul')
oUl.addEventListener('click',showTxt,true)
function showTxt(ev){
var obj = ev.target
if(obj.nodeName.toLowerCae() === 'li'){
console.log(obj.innerHTML)
}
}
</script>
</body>
</html>