javascript的性能优化通常包括两个方面,一是代码体积减小带来的下载时间减少,二是代码效率提高带来的执行时间减少。
关于代码体积的改善
javascript不同于c、c++这样的编译语言,开发人员不需要考虑变量名长度及注释等,因为在编译时这些都会被删除或替换。javascript的所有内容都需要下载到客户端执行,这些无意义的东西最好还是删除或缩小为妙。下面常用的减少代码体积的方法:
1、删除注释。
2、删除空格,制表符,换行符。
3、替换变量名。
当然,上面的三种情况不需要手工去做的,我们可以使用一些压缩工具JSMin,YUI Compressor,还有我正在用的Packer,TB Compressor等。经研究表明,即使是通过Gzip压缩后的代码,如果通过上面那些工具先进行一步压缩,仍可减少5%以上的体积。
4、避免使用布尔值。在js里true等于1,false等于0。
5、缩短否定检测。js代码里有许多检测某个值是否有效的判断语句,像
if(typeof g == undefined){
return;
}
if(g == null){
return;
}
if(g == false){
return;
}
if (g == 0){
return;
}
上面的语句除非变量g可以为0,或没有在作用域链中出现过,我们要用0和undefined判断,否则,这样写更简洁:
if (!g){
retrun;
}
关于代码效率的改善
=======================================(分割线,以上2009-07-08)
1、仔细检查函数中所有使用的变量,如果有一个变量不是当前作用域定义的,而且使用了不止一次,那么我们就应该把这个变量保存在局部变量中,而使用这个局部变量来进行读写操作。这样可以帮助我们将作用域外的变量的搜索深度减少到1。这对全局变量尤为重要,因为全局变量总是被放到作用域链的最后位置来搜索。
2、避免使用with语句。因为它会修改执行上下文(Execution Context)的作用域链,在最前面添加一个对象(Variable Object)。这就意味着在执行with的过程中,实际上的局部变量都被移到作用域链上的第二个位置,这会带来性能上的损失。
3、如果你确定一段代码肯定会抛出异常,那么就要避免使用try-catch,因为catch分支在作用域链上的处理方法和with是一样的。但try分支的代码是没有性能损失的,所以还是建议用try-catch来捕获那些不可预知的错误。
======================================(2009-06-19的分割线)
4、使用join代替“+”或“for”连接字符串。例如:
var wrap = [ vanillaDiv
, '<div style="position:absolute;top:0px;left:0px;padding:0px;'
, 'margin:0px;overflow:visible;height:'
, Dock.height, 'px;width:', Dock.width
, 'px;"></div></div>'
].join('');
现代浏览器对“+”连接字符串都做了相应的优化,可以根据应用情况和自己喜好权衡使用。这有文章Fastest way to build an HTML string,作者James Padolsey,一个很牛逼的18岁小孩。
5、将可反复利用的内容放在循环体外部。例如:
下面这个函数从DOM树中选择一个元素,然后对这个元素做一些事情。如果像下面这样,需要每次都从树中去找container这个元素,而且会每次都进行someArray的长度计算。
for(var i = 0; i < someArray.length; i++) {
var container = document.getElementById('container');
dosomething();
}
改进一下,写成下面这样,达到了同样的目的,却不用每次搜索DOM树了,也不用每次都计算someArray的长度了。
var container = document.getElementById('container');
for(var i = 0, len = someArray.length; i < len; i++) {
dosomething();
}
类似的应用还有地图蒙板这个例子,每次鼠标移动都要调用mbCtrl.move()这个函数,可以看一下index.js里的112-115行,如果直接这样用$(“#mapContainer > div:nth-child(3)”)选择器选择,每次鼠标移动都要搜索DOM树,实际应用时效率奇低呀。改进一下,
var mbCtrl = {
init:function(){
this.div3=$("#mapContainer > div:nth-child(3)");
},
move:function(){
this.div3=css({top:'0',left:'0',width:this.mwth,height:tc,display:"block",background:"#f00"});
}
}
在一开始就把要操作的对象选择出来,缓存起来,这样一个小改动,在一些浏览器(ie)里有成百倍的效率提高。
6、用{}代替new Object(),用[]代替new Array()。
这个不多说了,就是使用var obj={},arr=[]代替var obj=new Object(),arr=new Array(),这样就不用再进行一次实例化了,而且减少了代码体积。至于到底啥时候用object,啥时候用array,”A common error in JavaScript programs is to use an object when an array is required or an array when an object is required. The rule is simple: when the property names are small sequential integers, you should use an array. Otherwise, use an object.” – Douglas Crockford
=============================================(2009-07-03分割线)
7、if与switch的使用。if语句在js里应用很多,因为用户可能的操作习惯实在是没有规律,应该尽量减少判断执行次数。通常采用的优化方式有两种,最大可能排最前和分组判断。举个例子:
if (value > 3){
if (value == 5){
return result5;
}
else if (value == 4){
return result4;
}
else{
return result6;
}
}
else{
if (!value){
return result0;
}
else{
if (value == 1){
return result1;
}
else if (value == 2){
return result2;
}
else{
return result3;
}
}
}
让面的例子对if判断进行了分组,而且把最可能出现的判断条件放在了前面。当判断条件较多时,使用switch语句的效率要好于if语句,可以根据情况尽量使用switch语句处理。js里还有个好处就是case语句里可以使用任何类型的值,例如:
switch(value){
case "yellow":
alert('fuck');
break;
case "green":
alert('yada');
break;
case 3:
alert("what");
break;
default:
alert("go to hell");
}
这里还有个据说更好的方法:
switch(value){
case 0:
return result0;
case 1:
return result1;
case 2:
return result2;
case 3:
return result3;
case 4:
return result4;
case 5:
return result5;
}
var results = [result0, result1, result2, result3, result4, result5];
return results[value];
可以看出使用switch和数组达到了相同目的,但使用数组的效率更高一些。这里有个测试地址http://www.nczonline.net/experiments/javascript/performance/conditional-branching/
8、使用更有效率的循环写法。据说能节省50%的执行时间:
var values = [1,2,3,4,5];
var length = values.length;
//for loop
for (var i=length; i--;){
process(values[i]);
}
//do-while loop
var j=length;
do {
process(values[--j]);
} while (j);
//while loop
var k=length;
while (k--){
process(values[k]);
}
至于for-in循环,再对对象进行操作时很好用,但比较低效,所以除非要遍历对象,否则还是不要用它了。
9、优先使用javascript内置方法。js的内置方法都是在浏览器里的,通常都是由C++编译的,这比自己写的js方法要快很多很多。
10、最小化语句数量。对于js这种解释性语言,有理由相信语句越少,执行所需的时间越短。有几个例子:
var a = 3;
var b = "yada";
var c = [1,2,3];
var d = new Date();
var name = names[i];
i++;
var obj = new Object();
obj.color = "red";
obj.num = 5;
优化一下:
var a = 3, b = "yada", c = [1,2,3], d = new Date();
var name = names[i++];
var obj = {color:"red", num:5};