一:加载和执行
1.<script>标签放在页面底部,</body>闭合标签之前,这能确保在脚本执行前页面已经完成渲染。
2.合并脚本。页面中<script>标签越少加载越快。
3.无阻塞下载脚本:defer(延迟加载)、async(异步加载)、动态创建<script>元素下载并执行、使用XHR对象下载js代码并注入页面中。
二、数据存取
四种基本数据存储方式:字面量(基本类型:字符串、数字、布尔值、对象、数组、函数、正则式,直接赋值不用new的等式右边的值)、变量、数组项、对象成员。
1.访问字面量和局部变量最快,访问数组和对象成员相对较慢。
2.局部变量在作用域链的起始位置,因此访问局部变量比访问跨作用域变量更快。
3.避免使用with语句,它会改变执行环境作用域链,变量深度有变。
4.嵌套的对象成员少用,会明显影响性能。执行location.href 比Window.location.href快。
5.属性或方法在原型链中的位置越深,访问它速度越慢。
通常来说,可以把常用的对象成员、数组元素、跨域变量保存在局部变量中来改善js性能,因为局部变量访问速度更快。
对象字面量比new Object()更高效:{}字面量可立即求值,而new Object()本质上是方法调用,所以需要作用域解析,找到同名函数后创建对象。
对象字面量vs构造函数创建对象对比(字面量优势):
a.它的代码量更少,更易读;
b.它可以强调对象就是一个简单的可变的散列表,而不必一定派生自某个类;
c.对象字面量运行速度更快,因为它们可以在解析的时候被优化:它们不需要"作用域解析(scope resolution)";因为存在我们创建了一个同名的构造函数Object()的可能,当我们调用Object()的时候,解析器需要顺着作用域链从当前作用域开始查找,如果在当前作用域找到了名为Object()的函数就执行,如果没找到,就继续顺着作用域链往上找,直到找到全局Object()构造函数为止
d.Object()构造函数可以接收参数,通过这个参数可以把对象实例的创建过程委托给另一个内置构造函数,并返回另外一个对象实例,而这往往不是你想要的。
//对象直接量定义 var o = { propertyfun: function ([parameters]) {}, get property() {}, set property(value) {}, };
通过对象字面量和构造函数创建对象的区别: https://segmentfault.com/a/1190000008462406
三:DOM操作
1.最小化DOM访问次数,尽可能在JS端处理。(DOM渲染引擎和js引擎是独立的,交互消耗性能)如遍历.innerHTML+=‘a’赋值时,可先把’aa..’内容合并然后一次性赋值。
2.如果需要多次访问某个DOM节点,使用局部变量存储它的引用。
3.小心处理HTML集合(如document.images,document.links 返回a元素),因为它实时联系着底层文档。如果需要遍历时,集合长度缓存到变量,如果需要经常操作集合,可以复制到一个数组中。DOM标准中HTML集合以一种“假定实时态”实时存在,当底层文档对象更新时,它也会自动更新,即使获取集合元素个数也会执行询问过程。
4.如果可能的话,使用速度更快的API,如queryselectorall(返回NodeList ,返回的不是HTML集合,因此返回的节点不会对应实时的文档结构。而 document.getElementById()是HTML集合,需要把它拷贝到数组中才能得到类似qall类似的静态列表)和firstElementChild
5.注意重绘(非几何属性变化后引起。)和重排(几何属性和页面布局改变时。重排优化:通过队列化修改并批量执行来优化重排过程,但部分方法会强制刷新渲染队列,如offsetTop偏移量/scrollLeft滚动位置/clientWidth系列和计算出样式值getComputedStyle/currentStyle):最小化重排和重绘“”合并多次对DOM和样式的修改;批量修改样式时,“离线”操作Dom树(脱离文档:display,文档片段,复制节点),使用缓存布局信息(偏移量等),并减少访问局部信息的次数。
6.动画中使用绝对定位(减少刷新渲染队列),使用拖放代理()
7.使用事件委托(target/scrElement)来减少事件处理器的数量。
四:算法和流程控制
1. 在判断条件较多时,使用查找表比if-else和switch更快。
2. 浏览器调用栈大小限制了递归算法在js中的应用,栈溢出会导致代码中断。可改为迭代算法或Memoization(缓存运算结果的方法)来避免重复计算。
五:字符串和正则表达式
1.不考虑IE7数组项合并是比较慢的字符串连接方法,可用简单的+和+=操作符替代,避免不必要的中间字符串(IE除外,其他浏览器器会尝试为表达式左侧的字符串分配更多的内存,然后简单将第二个字符串拷贝到它的末尾。如果循环中基础字符串位于最左端,可以避免重复拷贝一个逐渐变大的基础字符串例: str +=’one’;str +=’two’可使用str=str+’one’+’two’提升性能,str作为基础,每次给它附加一个字符串。.但如果str =’one’+str+’two’优化失效)。
2.正则式处理好回溯问题
六:快速响应的用户界面
1.任何js任务都不应当执行超过100毫秒。过长运行时间导致UI更新出现明显迟缓影响用户体验。
2.可使用定时器分解大任务为小任务后执行,或使用WebWorker,它允许在UI线程外执行js代码,从而避免锁定UI。
七:快速响应的用户界面
1.选择正确的数据格式:纯文本和html只适用于特定场合,可节省客户端CPU周期;XML支持良好但它十分笨重且解析缓慢;JSON轻量级,解析速度快;字符分隔自定义格式十分轻量级,在解析大量数据集时非常快,但需要编写额外的服务端构造程序,并在客户端解析。
2.数据请求方式
XMLHttpRequest(XHR):不能从外域请求数据,从服务器端返回的数据被当做字符串或XML,这意味着处理大量数据将会很慢。不改变服务器状态,只获取数据的请求使用get,使用Get请求的数据会被缓存起来。参数超过2048个字符可使用POST请求。
Dynamic script tag insertion动态脚本注入:可跨域请求 var el=document.createElement(‘script‘);el.src=’’;(‘head’)[0].appendChild(el);
缺点:不能设置请求头,只能用get方式,不能设置请求超时处理或重试,是否失败也不一定知道。响应消息作为脚本标签的源码,它必须是可执行的js代码。
Iframes
Comet:
Beacons:类似动态脚本注入,使用js创建Image对象,把src设置为服务器上的脚本url。适合发送少量数据返回信息不太重要
var beacon= new Image(); beacon.src=url+’?key1=value1’; beacon.onload=function(){ if(this.width==1){ //如约定返回图片宽度为1表示成功2表示重试,如果无需返回数据返回一个204 no content 它将阻止客户端继续等待永远不会到来的消息正文 }else if(this.width==2){ //2否则重试 }} beacon.onerror=function(){}
Multipart XHR:允许客户端只用一个http请求就可以从服务端向客户端传送多个资源。数据不能缓存
注:任何数据类型都可以被js作为字符串发送。
3.减少请求数,可通过合并js、css文件或使用MXHR;
4.缩短页面加载时间,页面主要内容加载完成后,用ajax获取次要文件
八:编程实践
1.避免使用eval和Funciton构造器来避免双重求值(在js代码中执行另一段代码,此时首先会以正常方式求值,然后在执行过程中对包含字符串中的代码发起另一个求值运算)带来的性能消耗。给setTimeout和setInterval传递函数而不是字符串作为参数
2.避免重复工作(事件处理的兼容函数)。当需要检测浏览器时,可使用延迟加载(脚本调用时检测)和条件预加载(脚本加载期间提前检测)。
延迟加载:
function addHandler(target,eventtype,handler){ if(target.addEventListener){ addHandler=function(target,eventtype,handler){ target.addEventListener(eventtype,handler,false) } }else{ addHandler = function (target,eventtype,handler){ target.attachEvent(“on”+eventtype,handler) } } addHandler(target,eventtype,handler) }
条件预加载:
var addHandler = document.body.addEventListener? function(target,eventtype,handler){ target.addEventListener(eventtype,handler,false) } : function (target,eventtype,handler){ target.attachEvent(“on”+eventtype,handler) }
3.在进行数学计算时考虑使用自己操作数字的二进制形式的位运算
4.尽量使用原生方法。
九:构建并部署高性能js应用
1.合并js文件减少http请求量
2.压缩js文件
3.在服务端压缩js文件(Gzip编码)
4.通过正确设置http响应头来缓存js文件,通过改变文件名的方式强制浏览器重新加载指定文件
5.使用CDN(content Delivery networtk)内容分发网络提供js文件;CDN不仅可以提升性能,还能管理文件的压缩与缓存。
十:构建并部署高性能js应用
1.使用网络分析工具找出加载脚本和页面中其他资源的瓶颈,帮助决定哪些脚本需要延迟加载等
2.尽量减少http请求数,把脚本尽可能延迟加载
3.使用性能分析工具找出脚本运行过程中速度较慢的地方,检查每个函数所消耗的时间以及函数被调用的次数,通过调用栈自身提供的线索来找出需要集中精力优化的地方
其他:
应用实践中代码优化,提取重用函数,重用组件等。