我以为优化javascript是一门高深的学问,在这里也只能站在前人的肩膀上,说一些我浅显的认识,更但愿的是抛钻引玉,若有不对,敬请斧正。javascript
首先,要认识到是,优化js的关键之处在于,优化它的运行速度,以此为切入点。css
javascript的优化原则是:二八原则html
知足考量大多数状况,而遇到极端状况,有能力则兼顾之,学会放弃,适当取舍;前端
缘由是,影响用户的体验很重要的因素之一响应时间java
固然js优化只是提高响应时间须要改善的方面的众多之一,前端优化知识何其之深,只有深刻了解,才会惊讶于前端所须要掌握的知识竟是如此之多,固然无形之中也会给人压力||动力。扯远了,可是仍是想力荐两篇关于前端优化的blog:前端工程打开速度优化的按部就班总结 和 web前端优化最佳实践及工具。web
继续正题,ok,那么知道了目标是提高响应时间,加快运行速度,那么具体有哪些可行的方案呢:数组
管理做用域浏览器
举个板栗:缓存
var foo = 1;
function test(){
//对变量foo进行一系列操做
}
function test2(){
var foo = 1;
//对变量foo进行一系列操做
}
也就是说,局部变量存在于活动对象中,解析器只需查找做用域中的单个对象。app
在JavaScript中,咱们应该尽量的用局部变量来代替全局变量,这句话全部人都知道,但是这句话是谁先说的?为何要这么作?有什么根据么?不这么作,对性能到底能带来多大的损失?如下是我摘自《JavaScript Variable Performance》的一段:
在如何提升JavaScript性能这个问题上,你们最常听到的建议应该就是尽可能使用局部变量(local variables)来代替全局变量(global variables)。在我从事Web开发工做的九年时间里,这条建议始终萦绕在个人耳边,而且历来没有质疑过,而这条建议的基础,则来自于 JavaScript处理做用域(scoping)和标识符解析(identifier resolution)的方法。 首先咱们要明确,函数在JavaScript中具体表现为对象,建立一个函数的过程,其实也就是建立一个对象的过程。每一个函数对象都有一个叫作 [[Scope]]的内部属性,这个内部属性包含建立函数时的做用域信息。实际上,[[Scope]]属性对应的是一个对象(Variable Objects)列表,列表中的对象是能够从函数内部访问的。好比说咱们创建一个全局函数A,那么A的[[Scope]]内部属性中只包含一个全局对象(Global Object),而若是咱们在A中建立一个新的函数B,那么B的[[Scope]]属性中就包含两个对象,函数A的Activation Object对象在前面,全局对象(Global Object)排在后面。当一个函数被执行的时候,会自动建立一个能够执行的对象(Execution Object),并同时绑定一个做用域链(Scope Chain)。做用域链会经过下面两个步骤来创建,用于进行标识符解析。在执行JavaScript代码的过程当中,当遇到一个标识符,就会根据标识符的名称,在执行上下文(Execution Context)的做用域链中进行搜索。从做用域链的第一个对象(该函数的Activation Object对象)开始,若是没有找到,就搜索做用域链中的下一个对象,如此往复,直到找到了标识符的定义。若是在搜索完做用域中的最后一个对象,也就是全局对象(Global Object)之后也没有找到,则会抛出一个错误,提示用户该变量未定义(undefined)。这是在ECMA-262标准中描述的函数执行模型和标识符解析(Identifier Resolution)的过程,事实证实,大部分的JavaScript引擎确实也是这样实现的。须要注意的是,ECMA-262并无强制要求采用这种结构,只是对这部分功能加以描述而已。 了解标识符解析(Identifier Resolution)的过程之后,咱们就能明白为何局部变量的解析速度要比其余做用域的变量快,主要是因为搜索过程被大幅缩短了。
- 首先将函数对象[[Scope]]内部属性中的对象,按顺序复制到做用域链中。
- 其次,在函数执行时,会建立一个新的Activation Object对象,这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,这个Activation Object对象会被置于做用域链的最前面。
也就是:当标识符解析的过程须要进行深度搜索时,会伴随性能损失,并且性能损失的程度会随着标识符深度的增长而递增。
数据操做
1. 使用局部变量,它是最快的
obj.name比obj.xxx.name访问更快,访问属性的速度,与其在对象中的深度有关。“ . ”操做的次数直接影响着访问对象属性的耗时。
2. 缓存频繁使用的对象、数组及相关的属性值
function process(data){
var count = data.count;
if (count > 0){
for(var i = 0; i < count ; i++){
processData(data.item[i]);
}
}
}
3. 不直接操做NodeList,将其转换成静态数组后再使用
方法: Array.prototype.slice.call() => 标准浏览器
逐个拷贝到一个新数组中 => For IE
须要注意的是,遍历NodeList时,不作对当前NodeList相关结构有影响的DOM操做,而且如以前所提到的,要缓存一些频繁使用到的属性值,以避免发生没必要要的悲剧。板栗:
var divs = document.getElementsByTagName('DIV');
//假定页面中有div,因此divs.length是大于0的
for (var idx = 0; idx < divs.length; idx++){
document.body.appendChild(
//杯具悄然而置
document.createElement('DIV')
);
console.info(divs.length);
}
上面的代码最后运行会报错,缘由经过不断地往document.body下插入div 节点,for循环的终止条件( div.length也随之改变)失效,陷入死循环。也就是说经过getElementsByTagName()获取获得的是一个Live NodeList的引用,任何对其相关的DOM操做都会当即反应在这个NodeList上面。
Dom操做
1. 增删查改
2. 指明操做DOM的context
context.getElementsByTagName()
3. 拆分方法,一个方法解决一件事
拆分功能,让一个方法只作一件事,经过不断地调用方法来实现复杂功能,可是,这些简单方法要避免相互交叉调用。
Be Lazy(使脚本尽量少地运行,或者不运行。)
流控制
if(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
else{
}
原则:
var foo = 0;
if(foo == false){ //隐式转换
...
}
function recurse(){
recurse();
}
recurse(); //又是一个悲剧,会报错,无限递归了
Reflow
何为reflow,便是:在CSS规范中有一个渲染对象的概念,一般用一个盒子(box, rectangle)来表示。mozilla经过一个叫frame的对象对盒子进行操做。frame主要的动做有三个:
总的来讲,reflow就是载入内容树(在HTML中就是DOM树)和建立或更新frame结构的响应的一种过程。
那么形成reflow的缘由有:
因此若要要提升页面性能,其实就是避免reflow的开销,可是形成reflow的缘由有时候是为了完成交互效果而不可避免的,因此不能说彻底避免,只能尽最大限度的去减小,这就如我开头而言的二八原则。
如下是一些简单的指导方针能够帮助你页面上的回流(reflow)减到最小。