一、加快JavaScript文件的加载速度css
默认状况下,浏览器在解析页面时遇到JavaScript引用就会中止后续的HTML代码解析,直到等待JavaScript代码文件下载和运行完成后才继续解析剩余的HTML代码。推荐的作法是尽可能将JavaScript代码的引用放置在<body>的底部。数组
相比把脚本引用放置于页面底部的方式或动态建立script元素,给<script>设置defer和async属性的方式更优雅,它在兼顾代码可读性的同时也提升了代码加载执行的性能。defer就是在告诉浏览器JavaScript代码不会产生任何的页面内容,所以浏览器能够在加载此引用时继续解析页面后续的内容。async属性则代表能够以异步的方式加载和执行(不受限于顺序)JavaScript代码。浏览器
还能够按需加载JavaScript文件。按需加载的方式能更有效地减小初始加载的JavaScript代码量。推荐使用成熟的JavaScript加载框架,例如HeadJS、RequireJS、LABjs等JavaScript模块加载框架。缓存
二、养成良好的编码习惯,提升代码的运行速度性能优化
在编写JavaScript代码时,首先是保证代码的可读性和可维护,在此前提下才是选用高性能的编码方式。app
嵌套循环时把大循环做为内循环、尽可能避免循环内定义变量、在条件分支中建立只有在分支中才须要用到的对象、使用直接量代替对象、缓存计算结果减小重复计算等。框架
1)少用for-in循环异步
for-in循环提供了一种遍历对象属性的能力,但它的性能不好,应尽可能使用for循环代替。async
2)谨慎使用eval函数
3)正确使用数组
在索引时,一个混合了多种类型的数组将比类型单一的数组慢不少。所以,应使用数组保存类型单一的数据,而在其余状况下使用对象。
4)正确地内存回收
三、使用高性能的变量或属性值读取方式
做用域就是变量或函数的做用范围,JavaScript中最大的做用域就是全局做用域。最小的做用域是函数。
多个函数嵌套定义时,就会造成做用域包含的关系,这个关系成为做用域链。若一个函数在运行中遇到一个变量,就会从最近的做用域开始进行搜索,若是找到了就会使用这个变量,若是没有找到则会进入外层的做用域中查找,如此反复,直到找到此变量的定义,或者未找到而断定变量未定义为止。
一个变量在做用域链上查找的层级越多则读取的速度越慢,所以,函数中局部变量的访问是最快的,全局变量的访问是最慢的。为了提升变量的读取性能,最佳的实践是尽可能减小变量访问时在做用域链上查找的层级,最好将变量定义为本做用域的局部变量,尽可能不要定义全局变量。若是频繁地访问一个外做用域的变量,最好是用一个局部变量保存外部变量
另一个和做用域相关的性能损耗是with语句的使用。with表达式的使用也会造成一个做用域,无形中改变了上下文做用域链的深度。try-catch表达式的catch块中也会产生一个做用域。所以,最好将catch块中的处理交给一个函数,避免了在内部访问外部域的变量。
对象的属性读取也存在和变量读取时相似的性能影响,读取对象上的属性值时会搜索对象的原型链。
在JavaScript中,对象的构造函数中有一个名为prototype的对象,即为原型对象,这个对象上的属性和方法是共享给全部实例对象的。因此说,实例对象上的属性和方法来自于两个地方:自身和对应的原型对象。由于原型对象自己也能够是其余构造函数的实例对象,因此原型对象中的属性和方法也可能来自于其做为做为实例对象时对应的原型对象上。这就造成了一个由各原型对象组成的链条,成为原型链。原型链的顶端是构造函数Object中名为prototype的对象。
查找对象上的属性和方法,首先会查找自身是否存在此属性或方法,若是未找到,则会继续在原型链上查找,直到找到或未找到返回undefined值为止。
function Person (name){ this.name=name; } Person.prototype= {location: 'china'}; var personA = new Person('name1'); var personB = new Person('name2'); alter (personA.location); //china
alter (personB.location); //china
在原型链上检索的层级越多,性能越差,即便是读取在对象上直接定义的属性也比读取局部变量慢。所以,若是在代码中要频繁取得某个对象的属性值,尤为是此属性来自于对象上的原型对象上时,最佳的作法是把属性值缓存在局部变量中,提升读取对象属性的性能。
for (var i = 0;i < numbers.length; i++){ numbers[i] *= 2; } //性能改进的方案
for (var i = 0,len = numbers.length; i < len; i++){ numbers[i] *= 2; }
四、高效地DOM操做
文档对象模型(DOM)是一个独立于特定语言的应用程序接口。尽管DOM提供了丰富接口供外部调用,但DOM操做的代价很高,页面性能优化的一个主要的关注点就是DOM操做的优化。DOM操做优化的整体原则是尽可能减小DOM操做。
在浏览器中,DOM的实现和ECMAScript的实现是分离的。经过JavaScript代码调用DOM接口,至关与两个独立模块的交互。DOM操做对性能影响最大实际上是由于它致使了浏览器的重绘(repaint)和重排(reflow)。
浏览器的渲染原理:从下载文档到渲染页面的过程当中,浏览器会经过解析HTML文档来构建DOM树,解析CSS产生CSS规则树。JavaScript代码在解析过程当中,可能会修改生成的DOM树和CSS规则树。以后根据DOM树和CSS规则树构建渲染树,在这个过程当中,CSS会根据选择器匹配HTML元素。渲染树包括了每一个元素的大小、边距等样式属性,渲染树中不包括隐藏元素即head元素等不可见元素。最后浏览器根据元素的坐标和大小来计算每一个元素的位置,并绘制这些元素到页面上。
重绘指的是页面的某些部分要从新绘制,好比颜色或背景色的修改,元素的位置和尺寸并无改变;重排则是元素的位置或尺寸发生了改变,浏览器须要从新计算渲染树,致使渲染树的一部分或所有发生变化。重排的代价比重绘的代价高不少,重绘会影响部分的元素,而重排则有可能影响所有的元素。
以下的操做会致使重绘或重排:
1)增长、删除和修改可见DOM元素。
2)页面初始化的渲染。
3)移动DOM元素。
4)修改CSS样式,改变DOM元素的尺寸。
5)DOM元素内容改变,使得尺寸被撑大。
6)浏览器窗口尺寸改变。
7)浏览器窗口滚动。
在进行DOM操做时,遵循如下的最佳实践来下降因为重排或重绘带来的影响:
1)合并屡次的DOM操做为单次的DOM操做
element.style.borderColor = '#f00'; element.style.borderStyle = 'solid'; element.style.borderWidth = '1px';
//优化方案1
element.style.cssText += 'border: 1px solid #f00;'; //优化方案2
element.className += 'empty'; //HTML5:element.classList.add('empty');
2)把DOM元素离线或隐藏后修改
对脱离了页面布局流的DOM元素操做就不会致使页面的性能问题。
大批量修改DOM元素,具体的方式主要有如下3种:
(1)使用文档片断
文档片断是一个轻量级的document对象,并不会和特定的页面关联。
var fragment = docment.createDocmentFragment (); //大量基于fragment的DOM操做
... docment.getElementById('myElement').appendChild(fragment);
(2)经过设置DOM元素的display样式为none来隐藏元素
var myElement = document.getElementById('myElement'); myElenment.style.display = 'none'; //一些基于myElement的DOM操做
... myElement.style.display = 'block';
(3)克隆DOM元素到内存中
var old = document.getElementById('myElement'); var clone = old.cloneNode(true); //一些基于clone的DOM操做
... old.parentNode.replaceChild(clone, old);
3)设置具备动画效果的DOM元素的position属性为fixed或absolute
把页面中具备动画效果的DOM元素设置为绝对定位,使得元素脱离页面布局流,从而避免了页面的重排,只涉及动画元素自身的重排了。
4)谨慎取得DOM元素的布局信息
获取DOM元素的布局信息会有性能的损耗,若是存在重复调用。最佳的作法是尽可能把这些值缓存在局部变量中。
for (var i=0; i<len; i++) { myElement[i].style.top = targetElement.offsetTop + i*5 +'px'; }
//优化方案
var targetTop = targetElement.offsetTop; for (var i=0; i<len; i++) { myElement[i].style.top = targetTop + i*5 +'px'; }
另外,由于取得DOM元素的布局信息会强制浏览器刷新渲染树,而且可能会致使页面的重绘或重排,因此在有大批量DOM操做时,应避免获取DOM元素的布局信息,使得浏览器针对大批量DOM操做的优化不被破坏。若是须要这些布局信息,最好在DOM操做以前就取得。
var newWidth = div1.offsetWidth + 10; div1.style.width = newWidth + 'px'; var newHeight = div1.offsetHeight + 10; //强制页面重排
div1.style.height = newHeight + 'px'; //又会重排一次
//优化方案
var newWidth = div1.offsetWidth + 10; var newHeight = div1.offsetHeight + 10; div1.style.width = newWidth + 'px'; div1.style.height = newHeight + 'px';
5)使用事件托管方式绑定事件
在DOM事件上绑定事件会影响页面的性能,一方面,绑定事件自己会占用处理时间,另外一方面,浏览器保存事件绑定,绑定事件也会占用内存。在页面中绑定的事件越少越好。一个优雅的手段是使用事件托管方式,即利用事件冒泡机制,只在父元素上绑定事件处理,用于处理全部子元素的事件,在事件处理函数中根据传入的参数判断事件源元素,针对不一样的源元素作不一样的处理。
五、使用辅助工具优化JavaScript代码性能
性能优化必定要有目标和尺度,而不是靠感受。
在作JavaScript代码性能优化时,首先是查看JavaScript代码文件的加载状况。可使用各浏览器自带的开发工具。
不少性能检查工具也会检查代码文件的加载状况,并给出有价值的建议,如压缩代码、合并代码、调整代码加载顺序、延迟加载代码等。这类工具备PageSpeed、YSlow等。这类工具会从页面总体进行检查,而不是仅仅检查JavaScript代码文件。
高性能的Web网页,要求网页渲染过程当中达到理想的60帧/秒的帧率。