我与高性能js的约会

2019第一本书产出,记念一波:css


第一章:js加载

  1. js的阻塞特性:

当浏览器在执行js代码的时候,不能同时作其余事情。(界面ui线程和js线程用的是同一进程,因此js执行越久,网页的响应时间越长。)c++

  1. 脚本的位置

若是把脚本<script>放在<head>中,页面会等js文件所有下载并执行完成后才开始渲染,在这些文件下载和执行的过程当中:会致使访问网站的时候有明显的延迟,表现为:页面空白。程序员

这点在这个项目就用到了!ajax


特别是ie,说不定人家给你来个报错呢~(你能拿我怎么样。略略略.ipg)正则表达式

性能提高:推荐将全部的<script>标签尽量的放到<body>标签的底部,优先渲染页面,减小页面空白时间。算法

  1. 组件脚本。

每一个<script>标签初始下载的时候都会阻塞页面的渲染。性能提高作法:减小内嵌脚本:减小内嵌的<script>标签,将代码写在一个标签中。编程

合并外链的js文件:http请求会带来额外的性能开销,栗子:下载一个100KB的js文件比下载4个25kb的js文件更快。具体操做方法自行搜索。windows

  1. 无阻塞脚本的方法

script标签的aync属性:跨域

async 属性规定一旦脚本可用,则会异步执行。async 属性仅适用于外部脚本(只有在使用 src 属性时)。若是 async="async":脚本相对于页面的其他部分异步地执行(当页面继续进行解析时,脚本将被执行)数组

script标签的defer属性:

js文件在页面解析到script标签的时候开始下载,但并不会执行,dom加载完成执行。这两个属性的区别在于执行时机。

动态脚本元素。

js操做dom建立<script>标签,自定义生成标签的type、src属性。文件会在该元素被添加到页面的时候开始下载。ps:若是文件顺序很重要的话,最好按照顺序合成一个文件。而后再添加到页面中。这样:不管什么时候启动下载。文件的下载和执行过程不会阻塞页面的其余进程。

3. XHR ajax脚本注入、

用get请求一个文件,请求好了。而后建立动态脚本,最后添加进去。缺陷:文件要再请求页面的同一个域。

第二章:数据存取

1.字面量和局部变量速度快于数组和对象。

2.三目运算运算更快缘由:( cpu原理
  • CPU是经过流水线处理来得到高性能的。所谓流水线,简单来讲就是当CPU在处理当前指令的时候,后面已经有N条指令在后面排队等待你去执行了。这样,当你要执行下一条指令的时候,你不用再去找那条指令,它已经乖乖跟在前一条指令的屁股后面等你去执行了。
2.2 if…else…处理模式 那问题就在于,后面的指令须要确认一个排队顺序。若是程序员也是简单的编写流水线式的代码,对于CPU来讲指令排序很容易。可是if…else…就不同了。 if…else…简单来讲就是:当我知足条件,那我就执行A,若是我不知足条件,我就执行B。可是对于给指令排队的CPU来讲,它还没执行到判断条件这一步,不知道你满不知足呀!这样它就很差给指令排队了。 假设它按照你知足条件,把A指令排在你后面。那么当执行到最后发现你不知足条件,那么就得把以前排好的队列清空,从新给你把B指令排到后面给你执行。这种预测错误的惩罚将会致使CPU处理时间更长。 假设预测准确的话,每次调用函数大概13个时钟周期,而只要预测错误,可能就须要大约44个时钟周期了。


2.3 三元运算处理模式 对于三元运算符,它的处理方法就不一样了。 x=y>0?A:B; 当CPU要给这条指令排队时,它会将两种结果都进行排队,也就是表达式A和表达式B都会被CPU进行处理,算出结果。 计算对CPU来讲反而是它更喜欢的事情,你只要能排队排好了,让它能流水线模式来执行,对于它来讲整体速度反而更快。 CPU会将两种结果A和B都给计算出来(这跟if…else…不一样,其只会计算一种结果),最后再判断y>0?。若是y>0,则选择A,抛弃B; 不然就选择B,抛弃A。

3.执行函数的做用域链:
在执行函数过程当中,每遇到一个变量,都会经历一次标志符解析过程来决定从哪里获取或存储数据。该过程搜索执行环境的做用域链,从做用域链头部开始,从头至尾,若是没找到就会未定义(undefined).
Function add(num1,num2){
return num1 + num2;
}
函数访问sun1,sum2都会产生搜索过程。这个搜索过程影响性能。(名字相同的变量存在于做用域链不一样部分,搜索最早找到哪一个,就用哪一个。这个就会屏蔽其余的。)

3.解析标志符是会消耗时间的。 局部的读写速度老是快于全局的。由于全局在执行环境是最慢的;在执行环境的做用域链的最末端。

4. 减小跨做用域的值,把它保存到局部变量里面。
好比:document.getElementById(“go-btn”).onclick = function(){
start();
}
====> var doc = document;
====> var bd = document.body;
document 是全局对象,每次使用都要遍历整个做用域链,直到在最后的全局变量对象中找到。

还有在仓库中取的值,var ss = a.b.c.d.i.d.state;
vm.$post(url, params, prompt, function(source) {
  var rawData = source.orgInfoDetailList.data.basicInfo.rawDatas[0];
  if ( rawData
    && rawData.companyName
    && (rawData.orgNo || rawData.creditCode)
    && rawData.companyIndustry
    && rawData.industryCode
  ) {
  // doSomethingInteresting
    })
    vm.searchShow = false;
  } else {
    prompt.other('客户信息不完整');
  }
})复制代码
5.做用域链通常是不可变的,可是with能够作到。还有try catch;(反正我不用with)

6.var book = {
age:’78’,
sex:’女'
}
Console.log(book.hasOwnProperty(‘age’)) true

Console.log(book.hasOwnProperty(’toString’)) false

hasOwnProperty是用来断定是否包涵特定的实例成员。

要肯定是否包涵特定的属性,能够用in操做符。in会搜索实例也会搜索原型。
console.log(’title’ in book); true
console.log(’toString’ in book); true

7.嵌套成员越深,读取速度就会越慢。location.href < windows.location.href;若是这些属性不是对象的实例属性,那么成员解析还须要搜索原型链,花更多的时间。

8. . 和 [] 二者操做并无明显的区别。只有在safari中,点符号始终会更快。

第三章:dom编程


1.访问dom元素是有代价的——过桥费。修改元素更贵,致使浏览器从新计算页面的几何变化。

循环访问修改DOM元素绝逼是“大佬”!!!!!好吧,我是讽刺。


2.获取某个节点,代码更快更简洁的方式:
Var elements = document. querySelectorAll(‘#menu a’);
代替 document.getElementByTagName……

3.
DOM树:表示页面结构。
渲染树:表示DOM节点如何显示。(css)
一旦长和宽变化,或增长文字,元素的几何属性和位置受到影响。
就会从新构造渲染树———>>>
重排
接着浏览器从新绘制受影响的部分到屏幕中———>>>
重绘
并非全部的DOM变化都会影响几何。改变颜色的话只会来一次重绘,没有重排。

4.重排什么时候发生:
1.添加或删除可见的DOM元素。
2.元素位置改变。
3.元素尺寸改变。
4.内容改变,文本改变或者图片被另外一个图片替代。
5.页面渲染器初始化。
6.浏览器窗口尺寸改变。

5.offsetTop,scrollTop,clientTop……等等,须要返回最新的布局信息,所以浏览器不得不执行渲染列队中的“待处理变化”并触发重排以返回正确的值。就算以上属性没有任何改变。

6.
cssText 能够对某个节点进行多个样式修改。
ele.style.cssText = ‘border-left:1px;border-right:2px;border-bottom:9px’;
若是不想改变之前的,加一些渲染:ele.style.cssText+=‘border-top:20px’;

7.批量修改dom:经过一下步骤来减小重绘和重排的次数。
1.使元素脱离文档流:absolute,display…(隐藏元素,应用修改,从新显示)
2.对其应用多重改变:对要修改的文档拷贝拿出去修改,再放进来。
3.把元素带回文档中:在文档以外建立并更新一个文档片断,而后把它附加到原始列表中。

8.
通常来讲,重排只影响渲染树中的一小部分。
减小使用:hover

9.事件委托:
浏览器须要跟踪每一个事件处理器,这也会占用更多的内存。还有并非100%的按钮或连接会被用户点击。所以,使用事件代理,只须要给外层元素绑定一个处理器,就能够处理在其子元素上触发的全部事件。
捕获——>到达目标——>冒泡

还能够用来判断事件对象来源做相应处理。

第四章:算法和流程控制

1.
for(var prop in object){
//遍历全部属性名
}
不要使用for in 遍历数组成员

2.
for(var i=0;i<items.length;i++) 这样很差,由于每次循环都会再算一次item.length长度。

for(var i=0;len=items.length;i<len;i++) 这样有时能提高25%,甚至50%在ie里面。

使用 倒序循环能够优化50%~~60%.由于少了比较步骤和判断两步。
i- -操做自己会影响CPSR(当前程序状态寄存器),CPSR常见的标志有N(结果为负), Z(结果为0),C(有进位),O(有溢出)。i > 0,能够直接经过Z标志判断出来。i++操做也会影响CPSR(当前程序状态寄存器),但只影响O(有溢出)标志,这对于i < n的判断没有任何帮助。因此还须要一条额外的比较指令,也就是说每一个循环要多执行一条指令。

3.for 1139 forEach 1086. 其实差很少。


4.大多数状况下,switch优于if-else;可是若是条件很少仍是建议if-else,易读。

5.优化if-else:把可能性最大的放第一位。
If(value < 5){
//...
} else if (value > 5 && value < 10) {
//...
} else {
//…
}

6.
If (value === 0) {
//...
} else if (value === 1) {
//...
}else if (value === 2) {
//...
}else if (value === 3) {
//...
}else if (value === 4) {
//...
}else if (value === 5) {
//...
}else if (value === 6) {
//...
}else if (value === 7) {
//...
}else if (value === 8) {
//...
}else if (value === 9) {
//...
}else {
//...
}
这种状况最多可能要10次;因此换一种不稳定,可是提高性能的写法。

if(value < 6){
if (value < 3) {
If (value === 0) {
//...
} else if (value === 1) {
//...
}else {
//...
}
} else {
If (value === 4) {
//...
} else if (value === 5) {
//...
}else {
//...
}
}
} else {
不想写了。。。。。。。。。。你懂的,我懂的。
}

查找表—是个不错的选择。

当条件在1-4:三者均可以。但仍是不如查找表
            5-8:switch 优于 if,可是两者都不如查找表
            9以上switch 和 if 同样烂,都不如查找表
var results = [result0,result1,result2,result3,result4,result5,result6,result7,result8,result9,result10];
return results[value];


第五章:字符串和正则表达式


1.字符串连接:
array.Join() string.concat(),都要比+ +=效率高。

2.str = str + “one” + “two” 效率比 str += “one” + “two”;由于后者须要 建立临时变量去存“one” + “two”的值。

3.在正则表达式中,若是用^这个符号效率会更高,由于只要匹配到第一个失败的地方就会中止后面的继续匹配。(大知识)

4.去除字符串首尾空白:
s = ' oo ';
console.log(s.trim()); 原生js有自带trim.
能够本身实现trim()方法,
If (!String.prototype.trim) {
String.prototype.trim = function(){
return this.replace(/^s+/,””).replace(/\s+$/,”")
}
}

第六章: 快速响应的用户界面

1.浏览器限制:是必要的防止恶意代码永不中止的密集操做锁住用户的浏览器或计算机。分为调用栈大小限制和长时间运行脚本限制。100毫秒的响应时间让以用户体验良好。

2.定时器代码只有在建立它的函数执行完成以后,才有可能被执行。

3.若是延时时间到了,主程序尚未运行结束,那么延时代码就会在onclick代码执行期间就加入队列,那么onclick一旦执行完,就会立马执行延时代码,给人形成没有延时的错觉。通常至少25ms。

4.定时器可用来安排代码延迟执行,它使得你能够长时间运行脚本分解成一系列的小任务。


第七章: ajax


1.url长度超过2048个字符,用post.否则若是是get,请求的url被截断。

2.动态脚本注入:
1.优点:克服了XHR的最大限制:跨域请求数据。这是一个hack。
2.缺点:不能设置请求的头信息,参数传递也只能是get。

3.MXHR对于上传较多图片时性能提高4-10倍。

4.使用XHR发送数据到服务器时,get要比post更快,这是由于对于少许数据而言,一个get请求往服务器只发送一个数据包。一个post请求要发送两个数据包。一个装载头信息,另外一个装载post正文。post更适合发送大量数据到服务器,由于它不关心额外数据包的量,另外一个缘由是ie对url有限制,它不可能用过长的get请求。


第八章:编程实践


1.当在一段js代码中,执行执行另外一段js代码,都会致使双重求值的性能消耗。
好比:eval setTimeout setinterval
给 setTimeout setinterval传递函数而不是字符串做为参数。

2.使用object/array直接量:
var myobject = new Object();
myobject.name = ’selena’;
myobject.age = ‘12’;
myobject.flag = true;

var myobject = {
name:’selena’,
age:’12’,
flag:true
}

第二段比第一段更快。特别是数量越多,越明显。

3.原生方法老是比本身写的方法更快的,由于是用c++写的存在浏览器中的。这意味着这些方法会被编译成机器码,成为浏览器的一部分,不会像本身写的那样受限制。

我一直记得之前ACM大赛,朋友说要本身造轮子,要比原生更快。


这是以上比较简洁的总结啦,具体demo仍是夭折在个人小mac本里啦~

可是我的仍是以为,优化性能也要考虑代码的可读性,若是过于夸张的追求技术show,而忽略了的项目的可维护性,仍是不太好的~

相关文章
相关标签/搜索