js性能优化有哪些方法,提高js性能
如何优化JavaScript脚本的性能
由ShiningRay于2006年4月5日发布
作者:ShiningRay @ Nirvana工作室
随着网络的发展,网速和机器速度的提高,越来越多的网站使用富客户端技术。现在Ajax是最流行的方式。JavaScript是一种解释性语言,所以达不到C/Java的水平,这就限制了它在客户端能做什么。为了提高它的性能,我想根据我之前对JavaScript做过的多次测试,谈谈我自己的经验,希望能帮助你提高JavaScript脚本性能。
语言级
循环是一种非常常用的控制结构,大部分事情都要靠它来完成。在JavaScript中,我们可以使用for(;),while(),for(in)。其实这三个循环中for(in)的效率极差,因为需要查询哈希键,所以尽量少用。for(;)和while循环性能应该说基本上(平时使用时)相当。
其实这两个循环怎么用是很有讲究的。我在测试中有一些有趣的情况,见附录。最后的结论是:
如果循环变量增加或减少,不要单独给循环变量赋值,而是在最后一次读取时使用嵌套的or运算符。
如果要比较数组的长度,应该提前把数组的length属性放到一个局部变量中,减少查询次数。
局部变量和全局变量
局部变量的访问速度比全局变量快,因为全局变量实际上是全局对象的成员,而局部变量是放在函数的堆栈中。
不使用Eval
使用eval相当于在运行时调用解释引擎再次运行内容,需要花费大量时间。这时可以使用JavaScript支持的闭包来实现函数模板(闭包的内容请参考函数编程的相关内容)。
减少对象查找
由于JavaScript的解释性,a.b.c.d.e至少需要查询4次。先查A,再查A中的B,再查B中的C,依此类推。所以,这样的表达如果重复的话,应该尽量少。你可以使用局部变量,并把它们放在一个临时的地方进行查询。
这可以和循环结合起来,因为我们经常根据字符串和数组的长度来循环,通常这个长度是恒定的。比如我们每次查询a.length都要进行一次额外的操作,如果我们事先设置了var len=a.length,就少了一次查询。
字符串的连接
如果是追加字符串,最好用s=anotherStr,而不是s=anotherstr。
如果要连接多个字符串,应该使用less=,例如
s=a;
s=b;
s=c;
应该写成
s=a b c;
但是,如果您正在收集字符串,例如多次对同一字符串执行=操作,则最好使用缓存。怎么用?用JavaScript数组收集,最后用join方法连接,如下
var buf=new Array();
for(var I=0;i 100i ){
buf . push(I . tostring());
}
var all=buf . join( );
类型转换是一个常见的错误,因为JavaScript是一种动态类型语言,你不能指定变量的类型。
1.将数字转换为字符串并应用“1。虽然看起来有点丑,但其实这是最高效的。在性能方面:
(“”)字符串()。toString()新字符串()
其实这个和下面的“直接量”差不多。尝试使用编译时可以使用的内部操作比运行时使用的用户操作更快。
String()是一个内部函数,所以速度很快,而。toString()查询原型中的函数,所以比较慢。new String()用于返回精确的副本。
2.将浮点数转换成整数,这样更容易出错。很多人喜欢用parseInt()。实际上parseInt()是用来把字符串转换成数字的,而不是浮点数和整数之间的转换。我们应该使用Math.floor()或者Math.round()。
另外,与第二节的对象搜索问题不同,Math是内部对象,所以Math.floor()实际上并没有太多的查询方法和调用时间,速度是最快的。
3.对于自定义对象,如果定义了toString()方法进行类型转换,建议显式调用toString(),因为内部操作会在尝试了所有可能性之后,尝试对象的toString()方法是否可以转换为字符串,所以直接调用这个方法会更高效。
使用直接数量
其实这个影响比较小,可以忽略。使用直接量是什么意思?例如,JavaScript支持使用[param,param,]来直接表示一个数组。以前我们都是用new Array(param,param,).前者由引擎直接解释,后者调用数组内部构造函数,所以速度略快。
同样,var foo={}的方式比var foo=new Object()的方式要好;加油,var reg=/./;比var reg=new RegExp()更快。
字符串遍历操作
应该用正则表达式来循环字符串,比如替换和搜索,因为JavaScript本身的循环速度比较慢,正则表达式的操作是用C写的语言的API,性能不错。
高级对象以及Date和RegExp对象将花费大量时间来构建。如果可以重用,就应该缓存。
DOM相关
插入HTML
许多人喜欢使用JavaScript中的document.write来为页面生成内容。其实这是低效的。如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,设置他们的innerHTML,将自己的HTML代码插入到页面中。
用[""]查询比。items(),这和前面减少对象搜索的思路是一样的。打电话。items()添加了一个查询和函数调用。
创建DOM节点
通常,我们可能会使用字符串直接编写HTML来创建节点,但我们确实这样做了。
字符串操作效率低下。
因此,应该使用document.createElement()方法,而如果文档中有现成的模板节点,则应该使用cloneNode()方法,因为使用createElement()方法后,需要多次设置元素的属性,使用cloneNode()可以减少3354个属性设置。同样,如果您需要创建许多元素,您应该首先准备一个模板节点。
如果它的目的是运行代码,你应该使用setInterval而不是setTimeout。SetTimeout一次重置一个计时器。
据我测试,微软的JScript无论是执行速度还是内存管理,效率都比Mozilla的Spidermonkey差很多,因为JScript现在基本不更新了。但是SpiderMonkey不能使用ActiveXObject。
文件优化也是非常有效的手段。删除所有空格和注释,把代码放在一行可以加快下载速度。注意是下载速度而不是解析速度。如果是局部的,注释和空格不会影响解释和执行速度。
本文总结了我在JavaScript编程中发现的一些提高JavaScript性能的方法。事实上,这些经验基于几个原则:
直接拿现成的东西比较快,比如局部变量比全局变量快,直接变量比运行时构造的对象快等等。
尽可能减少执行次数,比如先缓存那些需要多次查询的。
尽可能使用语言的内置特性,比如字符串链接。
尽量使用系统提供的API,因为这些API都是编译好的二进制代码,执行效率很高。
同时一些基本的算法优化也可以用在JavaScript中,比如运算结构的调整,这里就不赘述了。但是因为JavaScript是解释型的,所以通常不会在运行时优化字节码,所以这些优化还是很重要的。
当然,其实这里的一些技巧在其他解释语言中也有使用,你也可以参考一下。
各种浏览器在http://www.umsu.de/jsperf/的测试和比较
http://home.earthlink.net/~kendrasg/info/js_opt/
由于是之前做的测试,测试代码不完整,我添加了如下部分:
var打印;
if(文档类型!=未定义){
print=function(){
document . write(arguments[0]);
}else if(typeof WScript!=未定义){
print=function(){
WScript。Echo(arguments[0],arguments[1],arguments[2]);
函数empty(){
功能基准(f){
var I=0;
var start=(new Date()).getTime();
而(我压力){
f(一);
var end=(新日期()).getTime();
WScript .回声(结束-开始);
开始=(新日期())。getTime();
而(i 60000){
c=[我,我,我,我,我,我,我,我,我,我,我];
我;
end=(新日期())。getTime();
WScript .回声(结束-开始);
开始=(新日期())。getTime();
而(i 60000){
c=新数组(我,我,我,我,我,我,我,我,我,我,我,我,我,我,我,我);
我;
var end=(新日期()).getTime();
WScript .回声(结束-开始);
函数内部演员(一){
返回”“我;
函数字符串转换(i){
返回字符串
函数新闻字符串播放(一){
返回新字符串
函数toStringCast(i){
返回I . tostring();
函数ParseInt(){
返回parse int(j);
函数MathFloor(){
返回数学。地板(j);
功能楼层(){
返回楼层(j)和:
定义变量压力=50000;
var a=
var floor=数学. floor
j=123.123
打印(- n字符串转换测试);
print(The empty:,benchmark(empty))。
print(intern:,benchmark(intern cast));
print( String:);
基准(字符串强制转换);
打印(新字符串:);
基准(newStringCast);
print( toString:);
基准(toStringCast);
打印(- n浮点到整数转换测试);
print( parse int )。
基准(parse int);
打印(数学。楼层’);
基准(数学楼);
打印("楼层")
基准(地板);
函数newObject(){
返回新对象();
函数internObject(){
return { };
打印(- n文字测试);
打印(运行时新对象,基准(新对象));
print(文字对象,benchmark(inter对象));
代码1:
for(var I=0;i i ){
arr[I]=0;
}
代码2:
var I=0;
while(i 100){
arr[I]=0;
}
代码3:
var I=0;
while(i 100){
arr[I]=0;
我;
}
在火狐浏览器下测试这两段代码,结果是代码2优于代码一和3,而代码一一般优于代码3,有时会被代码3超过;而在IE 6.0下,测试压力较大的时候(如测试10000次以上)代码2和3则有时候优于代码1,有时候就会远远落后代码1,而在测试压力较小(如5000次),则代码2代码3代码1。
代码4:
var I=0;
var a;
while(i 100){
a=0;
我;
}
代码5:
var a;
for(var I=0;i i ){
a=0;
}
上面两段代码在火狐浏览器和工业管理学(工业工程)下测试结果都是性能接近的。
代码6:
var a;
var I=0;
while(i 100){
a=I;
我;
}
代码7:
var a;
var I=0;
while(i 100){
a=I;
}
代码8:
var a;
for(var I=0;i i ){
a=I;
}
代码9:
var a;
for(var I=0;i ){
a=I;
}
这四段代码在火狐浏览器下6和8的性能接近,7和9的性能接近,而6, 8 7, 9;
最后我们来看一下空循环
代码10:
for(var I=0;我我){ }
代码11:
var I;
while(I 100){ I;}
最后的测试出现了神奇的结果,火狐下代码10所花的时间与代码11所花的大约是24:1。所以它不具备参考价值,于是我没有放在一开始给大家看。