由于JavaScript语言的单线程和解释执行的两个特点决定了它本身有很多地方存在性能问题,因此需要进行性能优化的地方还是很多,下面就JavaScript存在的性能障碍节点进行说明。
(1)eval问题
比较下面两段代码:
//方法1
var reference={},props=/"p1/";
eval(/"reference./"+props+/"=5/")
//方法2
var reference={},props=/"p1/";
reference[props]=5
没有eval的代码要比有eval的代码快100倍以上,这是因为JavaScript代码在执行前会进行类似预编译的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var声明的变量设置为活动对象的属性,但此时这些变量都是undefined,还会将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。如果使用了eval,那么eval中的代码(实际上为字符串)无法预先识别其上下文,无法被提前解析和优化,即无法进行预编译的操作,所以,代码性能也会大幅度降低。
(2)Function用法
比较下面两段代码:
//方法1
var func1=new Function(/"return arguments[0]+arguments[1]/");
func1(10,20);
//方法2
var func2=function{
return arguments[0]+arguments[1]
};
func2(10,20);
第一段代码的效率会比第二段代码的效率差很多,故推荐使用第二种方式。
(3)字符串拼接
经常看到如下形式的简单字符串拼接代码:
str+=/"str1/"+/"str2/"
这是拼接字符串常用的方式,但这种方式会有一些临时变量的创建和销毁,使性能受到影响,所以推荐使用如下方式拼接:
var str_array=;
str_array.push(/"str1/");
str_array.push(/"str2/");
str=str_array.join(/"/");
这里利用数组(array)的join方法实现字符串的拼接,尤其是在早期的IE版本(如IE 6)上运行时,会有非常明显的性能上的改进。
当然,最新的浏览器(如Firefox、IE 8及其以上版本等)对字符串的拼接做了优化,也可以这样实现字符串快速拼接:
str+=/"str1/"
str+=/"str2/"
新的浏览器对“+=”做了优化,其性能略好于数组的join方法。在不久的将来,更新版本的浏览器可能对“+”进行优化,那时可以直接写成:str+=/"str1/"+/"str2/"。
(4)隐式类型转换
参考如下隐式类型转换代码:
var str=/"12345678/",arr=;
for(var i=0;i<=s.length;i++){
arr.push(str.charAt(i));
}
在上面代码中,每次循环时都会调用字符串的charAt方法,但由于将常量“12345678”赋值给str,因此这里的str并不是一个字符串对象,每次调用charAt方法时都会临时构造值为“12345678”的字符串对象,然后调用charAt方法,最后再释放这个字符串临时对象。可以通过一些改进来避免隐式类型转换。
var str=new Stirng(/"12345678/"),arr=;
for(var i=0;i<=s.length;i++){
arr.push(str.charAt(i));
}
这样,变量str作为一个字符串对象,就不会有这种隐式类型转换的过程了,效率会显著提高。
(5)字符串匹配
JavaScript具有RegExp对象,支持对字符串的正则表达式匹配。它是一个很好的工具,但性能并不是非常理想。相反,字符串对象(String)本身的一些基本方法的效率是非常高的,如substring、indexOf、charAt等,在需要用正则表达式匹配字符串时,可以考虑下面的因素。
❑是否能够通过字符串对象本身支持的基本方法解决问题。
❑是否可以通过substring来缩小需要用正则表达式的范围。
这些方式都能够有效地提高程序的运行效率。关于正则表达式对象,还有一点需要注意:
for(var i=0;i<=str_array.length;i++){
if(str_array[i].match(/^s*extras/)){
//...
}
}
在这里向match方法传入/^s*extras/是会影响执行效率的,因为在这一过程中会构建临时值为/^s*extras/的正则表达式对象,执行match方法,然后销毁临时的正则表达式对象。可以这样进行优化:
var sExpr=/^s*extras/;
for(var i=0;i<=str_array.length;i++){
if(str_array[i].match(sExpr)){
//...
}
}
这样就不会有临时对象了。
(6)setTimeout和setInterval
setTimeout和setInterval这两个函数可以接受字符串变量,但会带来和之前谈到的eval类似的性能问题,所以建议还是直接传入函数对象本身。
(7)利用提前退出
参考如下两段代码:
//代码1
if(source.match(//)){
}
//代码2
if(name.indexOf&&source.match(//)){
}
代码2中多了一个对name.indexOf的判断,这使程序每次执行到这一段时会先执行indexOf的判断,再执行后面的match,在indexOf比match效率高很多的前提下,这样做会减少match的执行次数,从而在一定程度上提高效率。