编写高效JavaScript
当今的Web应用程序都是由大量的JavaScript代码驱动。每次用户在界面上操作时,都要执行成千上万行的Js代码。因此,关注性能,不仅要关注页面的加载时间,也要关注在页面上操作时的响应速度。要实现快速而友好的用户界面,最好的办法就是编写的JavaScript代码能在浏览器中高效的运行。
在编写代码的过程中,如果多注意一下自己的代码质量,就可以得到更流畅的用户体验。身为一名前端开发攻城狮,一手漂亮的代码也是成长进阶的必备技能。下面是看书的几点总结:
1.使用局部变量
到目前为止,局部变量是JavaScript中读写最快的标识符。因为它存在于执行函数的活动对象中(上一篇文章已经进行总结),解析标识符只需要查找作用域链中的单个对象。读取变量值的总耗时随着查找作用域链的逐层深入而不断增长,所以标识符越深读取速度越慢。在编写代码时,能用局部变量的就尽量使用局部变量,不但可以提升代码执行速度,还可以避免全局变量的污染,一举两得。
2.高效的存取数据
数据在脚本中存储的位置直接影响脚本执行的总耗时。一般而言,在脚本中有4种地方可以存取数据:
字面量值;
变量;
数组元素;
对象属性;
在大多数浏览器中,从字面量中读取值和从局部变量中读取值的开销差异很小,以至于可以忽略;真正的差异在于从数组或对象中读取数据。存储这些数据结构中的某个值,需要通过索引(对于数组)或属性值(对于对象)来查询数据存储的位置。请看下面代码:
function process(data){if(data.count > 0){for(var i = 0; i< data.count ; i++){processData(data.item[i]);}}}
这段代码多次读取data.count,如果将值存储到局部变量中,函数将运行的更快:
function process(data){Var count = data.count;if(count > 0){for(var i = 0; i < count ; i++){processData(data.item[i]);}}
}
改写后的执行过程中读取data.count仅有一次。由于减少了查找对象属性的次数,函数的执行效率将比之前的更高(当然还可以进一步优化,将在下面进行说明)。随着数据结构的增加,它对数据存储速度的影响也跟着变大,例如,存取data.count比data.item.count快,存储data.item.count比data.item.subitem.count快。在处理属性时,点符号(.)的使用次数直接影响存取该属性的总耗时。
在处理HTMLCollection对象(比如getElementsByTagName这样的DOM方法返回的值,或element.childNodes这样的属性值)时使用局部不变量特别重要。实际上,每次存取HTMLCollection对象的属性时,都会对DOM文档进行动态查询。例如:
var divs = document.getElementsByTagName("div");
for (var i = 0; i< divs.length ; i++){ //避免!var div = divs[i];process(div);
}
这段代码每次通过属性名或索引存取divs属性时,DOM实际上重复执行了一次对页面上所有<div>元素的查询;在这段代码中,每次读取divs.length或divs[i]都会对页面查询一次。因此应尽可能把这类值存储在局部变量中,避免多次重复插叙,例如:
var divs = document.getElementsByTagName("div");
for( var i = 0, len = divs.length; i< len; i++ ){ //更好的方式var div = divs[i];process(div);
}
这样就减少了直接存取该对象的次数。
3.流控制
除了数据存取之外,流控制或许是提升JavaScript性能最重要的一环。在一个if-else语句中,语句的执行流越深,需要判断的条件就越多。
3.1快速条件判断
if语句:看下面的例子:
if(value == 0){return result0;
}else if(value == 1){return result1;
}else if(value == 2){return result2;
}else if(value == 3){return result3;
}else if(value == 4){return result4;
}else if(value == 5){return result5;
}else if(value == 6){return result6;
}else if(value == 7){return result7;
}else if(value == 8){return result8;
}else if(value == 9){return result9;
}else {return result10;
}
通常这样类型的结构是不被推荐的,它的主要问题在于:语句的执行流越深,需要判断的条件就越多。当value为9是,执行完成的事件会比value为0时要长,因为它之前的所有条件都需要判断。尽管使用大量的if条件语句是不可取的,但我们可以采取以下几个方法来提高整体性能:
第一种方法是将条件按照频率降序排列。由于最快的运算在第一个条件语句后就退出,所以要确保这种情况尽可能多地出现。假设前面例子中的最常见的情况时value 等于5,其次是value等于9,这就意味着,执行流在达到最常见的情况之前要进行5次条件判断,在达到第二常见的情况之前要进行9次判断。尽管按数字递增的方式排序更容易阅读,但实际上把它写成下面这个样子执行效率更高:
if(value == 5){return result5;
}else if(value == 9){return result9;
}else if(value == 0){return result0;
}else if(value == 1){return result1;
}else if(value == 2){return result2;
}else if(value == 3){return result3;
}else if(value == 4){return result4;
}else if(value == 6){return result6;
}else if(value == 7){return result7;
}else if(value == 8){return result8;
}else{return result10;
}
另一种方法是优化if语句的条件,将条件分成几个分支,下面的二分查找算法可以逐步找出有效的条件,在条件数量众多,且没有出现频率特别高的条件,从而不能简单的按频率排列的情况下,这个方法可取。
if(value < 6){if(value < 3){if(value == 0){return result0;}else if(value == 1){return result1;}else if(value == 2){return result2;}}else{if(value == 3){return result3;}else if(value == 4){return result4;}else{return result5;}}
}else{if(value < 8){if(value == 6){return result6;}else{return result7;}}else{if(value == 8){return resdult8;}else if(value == 9){return result9;}else{reutrn result10;}}}
这段代码确保在任何情况下,都不会超过4次的条件判断。
然而问题依然存在,每增加一个条件最终都会导致执行时间变长,这影响性能和可维护性。这是switch语句就有了用武之地。
switch语句:switch语句简化了多重条件判断的结构,并提升了性能:
switch(value){case 0:return result0;break;case 1:return result1;break;case 2:return result2;break;case 3:return result3;break;case 4:return result4;break;case 5:return result5;break;case 6:return result6;break;case 7:return result7;break;case 8:return result8;break;case 9:return resutl9;break;default:return result10;
}
Firefox可以很好的处理switch语句,每个条件判断执行的时间大致相同,与他们定义的顺序无关。然而其他浏览器做的不怎么好。在IE、Opera、Safari和Chrome中,switch语句越深,执行的时间增加越明显。不过这比每个条件都用if语句要增加的少。
在JavaScript中,当仅判断一两个条件时,if语句通常比switch语句更快。当有两个以上条件且条件比较简单(不是进行范围判断)时,switch语句往往更快。这是因为大多数情况下,switch语句中执行单个条件所需时间比在if语句中短。所以当有大量的条件判断时,使用switch语句更合适。
数组查询:在JavaScript中,处理条件判断的解决方案不止两种。除了if语句和switch语句外,还有第三种方法:在数组中查询值:
//定义数组results
var results = [result0,result1,result2,result3,result4,result5,result6,result7,result8,result9,result10];
//返回正确结果
return results[value];
不使用条件语句,而是把所有的结果都存储在数组中,并用数组的索引映射value变量。检索对应的结果就是简单的查询数组值。虽然查询数组的耗时也会随进入数组的深度而增加,但是和每个条件都用if语句或者switch语句来判断相比,增加的时间还是要小得多。使用数组查询的理想情况时有大量的条件存在,并且这些条件都能用数字或字符串(对于字符串,可以使用对象而非数组来存储)这样的离散值来表示。
使用数组查询少量的结果是不合适的,因为数组查询往往比少量的条件判断语句慢。当要查询的条件范围很大时,数组查询才会变得非常有用。
最快的条件判断
前面列出的3种技术:if语句、switch语句和数组查询,在代码优化执行方面都有各自的用处:
- 使用if语句
两个之内的离散值需要判断;
大量的值能容易地分到不同的区间范围中。
- 使用switch语句
超过两个而少于10个离散值需要判断;
条件值是非线性的,取法分离出区间范围。
- 使用数组查询
超过10个值需要判断;
条件对应的结果是单一值而不是一些列操作。
3.2快速循环
在 JavaScript中,循环是导致性能问题的常见起因,编写循环的方式能彻底改变它的执行时间。看下面的例子:
//未优化的代码
var values = [1,2,3,4,5];//for循环for(var i = 0; i< values.length; i++){process(values[i]);
}
最显眼的是循环中反复比较变量与数组的长度,在前面的例子中已经提到,查找属性要比存取局部变量更耗时优化一下是这样:
//第一步优化var values = [1,2,3,4,5];
var length = values.length;for(var i = 0; i< length; i++){process(values[i]);
}
另外一种提高性能的简单方式是将循环变量递减到0,而不是递增到总长度。根据每个循环的复杂度不同,这个简单的改变可以比原来节约多达50%的执行时间。例如:
var vaules = [1,2,3,4,5];
var length = values.length;for(var i = length; i--; ){process(values[i]);
}
因为结束条件被改造为与0进行比较(注意一旦循环变量等于0,结束条件就会变为假)。执行效率更高了。
避免 使用for-in循环
JavaScript中的for循环,while循环,do-while循环的执行效率差不多。但是for-in循环的效率差异还是很大的。它用来遍历JavaScript对象的可枚举属性。典型的使用方法:
for(var prop in object){if(object.hasOwnProperty(prop)){ //确保只处理实例自身的属性process(object[prop]);}
}
这段代码遍历了给定对象的属性。它的结束条件无法改变(除非使用throw抛异常,然后手动捕获),而且遍历属性的顺序也无法改变。此外for-in循环需要从对象中解析每个可枚举的属性,为了提取这些属性需要检查对象的原型和整个原型链。遍历原型链就像遍历作用域链,它会增加耗时,从而降低整个循环的性能。因此尽可能地避免使用for-in循环可以让代码执行的更快速。
编写高效JavaScript相关推荐
- 【读书笔记】《编写高效的JavaScript程序》
为什么80%的码农都做不了架构师?>>> 看到一篇文章,http://www.csdn.net/article/2012-11-20/2811887-writing-fast- ...
- 编写高效的JavaScript程序
转载自http://kb.cnblogs.com/page/168162/ 英文原文:Writing Fast, Memory-Efficient JavaScript Addy Osmani是谷歌公 ...
- [实战分析] 编写高效的JavaScript程序
摘要:有人说想要快速的加载Web网页就如同汽车一样,需要使用特殊工具.想知道JavaScript如何在V8中工作的吗?如何避免性能中出现的陷阱?当涉及到编写高效的内存和快速创建代码时总会出现一些常见的 ...
- 编写高性能JavaScript
编写高性能JavaScript 文本来源:点击打开链接(这个排版比较舒服) 本文来源:http://www.admin10000.com/document/4988.html 很多JavaScript ...
- 编写高效的jQuery代码
编写高效的jQuery代码 最近写了很多的js,虽然效果都实现了,但是总感觉自己写的js在性能上还能有很大的提升.本文我计划总结一些网上找的和我本人的一些建议,来提升你的jQuery和javascri ...
- 《PHP精粹:编写高效PHP代码》——2.1节数据持久化和Web应用程序
本节书摘来自华章社区<PHP精粹:编写高效PHP代码>一书中的第2章,第2.1节数据持久化和Web应用程序,作者:(美) Davey Shafik,更多章节内容可以访问云栖社区" ...
- 编写高效的Android代码
编写高效的Android代码 转自:http://www.chinaup.org/docs/toolbox/performance.html 介绍 对于如何判断一个系统的不合理,这里有两个基本的原则: ...
- 《PHP精粹:编写高效PHP代码》——第1章面向对象编程
本节书摘来自华章社区<PHP精粹:编写高效PHP代码>一书中的第1章面向对象编程,作者:(美) Davey Shafik,更多章节内容可以访问云栖社区"华章社区"公众号 ...
- 转:编写高效的Android代码
毫无疑问,基于Android平台的设备一定是嵌入式设备.现代的手持设备不仅仅是一部电话那么简单,它还是一个小型的手持电脑,但是,即使是最快的最高端的手持设备也远远比不上一个中等性能的桌面机. 这就是为 ...
最新文章
- 数据不够,是模型表现不佳的“借口”,还是真正的问题所在?
- VS 2013 C++ 类模板定义与实现 进行分离
- 智慧交通day04-特定目标车辆追踪03:siamese在目标跟踪中的应用-汇总
- Linux系统定时任务Crond
- [已解决] org.hibernate.HibernateException:未配置CurrentSessionContext
- 阶段3 1.Mybatis_04.自定义Mybatis框架基于注解开发_3 基于注解的自定义再分析
- Intent与intent-filter
- c语言书籍(c语言书籍)
- 单片机步进电机正反转C语言程序,单片机控制步进电机正反转
- 逆向分析CRACKME 第一章 Acid burn
- Java入门-换行输出
- 二维彩虹二维码产品功能更新:增加“赞赏”、“重置”功能
- SpringBoot 代码结构
- 【机器学习】目标函数总结
- java如何设置jlabel位置_Java Swing – JLabel位置
- Godaddy 添加子域名
- 少年中国说——梁启超〔近现代〕
- C4D制作lowpoly风格物体
- 个人网站搭建(Day 8)— Django-simditor的使用以及代码高亮
- Allegro加密PCB文件