广交好友~~ 想要讨论的可以留下msn~~~ 希望群友网友经常能提出问题,一起解决,共同提高

javascript性能优化

上一篇 / 下一篇  2010-12-23 17:52:25 / 个人分类:Java&C


很久就想总结一下关于javascript性能优化方面的一些东西,平时也有注意收集这方面的资料。把del.icio.us里的收藏的东西翻出来看一遍,才惊奇地发现,这些所谓的优化方法大多出自《javascript高级程序设计》一书,当然也有个别
不一样的。总之这本书上关于javascript性能优化的内容足足用了近20页来进行阐述,所以今天我也照本宣科地来介绍一下,同时收录其他方法。

javascript的性能优化应分为两部分:下载时间和执行时间。

下载时间

javascript作为一种解释性语言不同于其他编程语言。在诸如java,c,c++的语言中,开发人员根本无需考虑变量名的长度以及长篇大论的注释,因为这些在编译时都会被删除。

但是web浏览器下载的是javascript的源代码,你编写的javascript程序文件会原模原样地下载到客户端。而这些长变量名和注释 就会影响脚本的下载时间。单个TCP-IP包中能放入的字节数是1160,所以最好将每个javascript文件的大小控制在1160字节以内以获取最 优的下载时间。

javascript文件中的任何字节,不管是空格,还是换行都会影响javascript文件的下载时间,所以在部署任何javascript文件之前,都应该尽可能地优化脚本文件的体积。以下是一些常用的优化javascript文件大小的方法:

1、删除注释
脚本中的任何注释都应该在部署之前被删除。虽然注释对于开发人员来说意义重大,但在部署时这些注释对客户端用户是没有任何实质意义的,而更可怕的 是很多javascript源代码中的注释会比代码多得多。因此删除注释是所见javascript文件大小最为方便有效的途径。

2、删除制表符和空格
又规律地缩进代码有增强代码的可阅读性,但是浏览器/客户端用户并不需要这些额外的制表符和可个,所以最好删除它们,包括函数参数,赋值语句和比较操作符之间的空格:

function doSomething ( arg1, arg2, arg3 ){
 alert(arg1 + arg2 + arg3);
}
function doSomething(arg1,arg2,arg3){alert(arg1+arg2+arg3);}3、删除所有换行
和前一条规则一样,只要你在程序的每行结尾都正确地添加了分号,那么就不需要再添加换行符了。

4、替换变量名
此方法很无聊,大致做法就是将所有变量名替换成无意义的简短变量名

要手动地完成以上四步,在实际中可能会有一定的难度,那么下面的一些链接可能对你有所帮助。

ECMAScript. Cruncher:http://saltstorm.net/depo/esc/
JSMin(The JavaScript. Minifier): http://www.crockford.com/javascript/jsmin.html
Online JavaScript. Compressor:http://dean.edwards.name/packer/

5、替换布尔值
对于比较来说,true等译1,false等于0。因此可以将字面量的true都用1来替换,而false用0来替换,进而较少一定的字节数。

6、缩短否定检测
代码中常常会出现检测某个值时候有效的语句。而大部分否定测试所做的就是判断某个变量是否为undefined、null或者false,如下:

if(oTest != undefined){
 // dosomething
}
if(oTest != null){
 // dosomething
}
if(oTest != false){
 // dosomething
}
//这样写更简洁
if(!oTest){
 // dosomethin
}7、使用数组和对象字面量

var aTest = new Array;
var Test = new Object;
oTest.pro1 = "pro1"
oTest.pro2 = "pro2";和下面的代码作用是完全相同的,但是下面的代码可以节省更多的字节。

var aTest = [];
var Test = {pro1:"pro1",pro2:"pro2"};执行时间


另外


javascript是一种解释性语言,它的执行速度要大大慢于编译型语言。据测试,javascript的执行效率要比编译型的C程序慢 5000倍;比解释型的Java慢100倍;比解释型的Perl慢10倍。但是我们仍然可以通过一些简单的事情来提高javascript的效率,同时这 也显得更加重要。

1、使用局部变量
在函数中,总是使用var来定义变量。无论何时使用var都会在当前的范围类创建一个局部变量。如果不使用var来定义变量,那么变量会被创建在 window范围内,那么每次使用这个变量的时候,解释程序都会搜索整个范围树。同时全局变量要在页面从浏览器中卸载后才销毁,而局部变量在函数执行完毕 即可销毁,过多的全局变量增加了不必要的内存消耗。

2、避免使用with语句
使用with语句能够减少一定的代码长度,但是在使用with语句时,要强制解释程序不仅在范围树内查找局部变量,还强制检测每个变量及指定的对象,看其是否为特性。因为,我们也可以在函数中定义同明的变量。

3、选择正确的算法
只要有可能就应该用局部变量或者数字索引的数组来替代命名特性。如果命名特性要多次使用,就先将它的值存储在局部变量中,一避免多次使用线性算法请求命名特性的值。

var aValues = [1,2,3,4,5,6,7];
function testFunc(){
 for(var i=0, iCount=aValues.length; i++){
  alert(i + "/" + iCount + "=" + aValues[i]);
 }
}4、反转循环
循环在各种编程语言中得到大量应用,所以保持循环的高效性尤为重要。按照反向的顺序进行循环迭代是一种有效的方法。

for(var i=aValues.length-1; i >= 0; i--){
 //do something
}反转循环有利于减低算法的复杂度。它用常数0作为循环控制语句以减小执行时间。

5、翻转循环
用do..while循环来替代while循环以进一步减少执行时间。假设有如下while循环:

var i=0;
 while(i < aValues.length){
  //do something
 i++;
}可用do..while循环重写上面的代码而不改变行为:

var i=0;
do{
 //do something
 i++;
}while(i < aValues.length)这段代码比用while循环更快,因为它用循环反转来进一步地优化:

var i=aValues.length-1;
do{
 //do something
 i--;
}while(i>=0)也可以将自减操作直接放进控制语句中,以减少额外的语句。

var i=aValues.length-1;
do{
 //do somethin
}while(--i>=0)这个循环已经被完全优化了

6、展开循环
可以考虑将循环展开,一次执行多个语句。考虑如下for循环例子:

var sum = 0;var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
for(var i=0; i<aValues.length; i++){
 sum+=aValues[i];
}循环总共要执行20次,每次都是对变量sum进行增量操作,但是可以这样写:

var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var sum = 0;
for(var i=0; i<aValues.length; i++){
 sum+=aValues[i];
 i++;
 sum+=aValues[i];
 i++;
 sum+=aValues[i];
 i++;
 sum+=aValues[i];
 i++;
 sum+=aValues[i];
 i++;
}在循环体内做了五次增量。每次增量后,都对变量i加1,所以多数组的遍历与原来是一致的,但是控制语句只执行了四次,减少了执行时间。当然还可以继续优化:

var aValues=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var sum = 0;
for(var i=0; i<aValues.length; i++){
 sum+=aValues[i++];
 sum+=aValues[i++];
 sum+=aValues[i++];
 sum+=aValues[i++];
 sum+=aValues[i++];
}关于此算法的更多信息可以参考这里http://home.earthlink.net/~kendrasg/info/js_opt/

7、优化if语句
使用if语句和多个else语句时,一定要把最有可能的情况放在第一个,然后是第二可能出现的情况,如此排列,这样就减少了要进行多次测试才能遇到正确条件的情况。

同时也要尽量减少else if语句的数量,并且将条件按照二叉树的方式进行排列。例如:

if(iNum>0){
if(iNum>10){
 alert("Between 0 and 10");
}else{
 if(iNum>20){
  alert("Between 10 and 20");
 }else{
  if(iNum<30){
   alert("Between 20 and 30");
  }else{
   alert("Greater than or equal to 30");
  }
 }
}
}else{
 alert("Less than or equal to 0");
}8、switch和if
一般来说超过两种情况时,最好使用switch语句。常用switch来代替if语句,最高可令执行快10倍。在javascript中就更加可以从中获益,因为case语句可以使用任何类型的值。

9、避免字符串连接
一旦一次要使用多个字符串的连接(比如,大于五个),最好使用如下方式:

var buf = new Array();
for(var i = 0; i < 100; i++){
 buf.push(i.toString());
}
var all = buf.join("");10、优先使用内置方法

只要可能,就应该考虑优先使用内置方法。因为内置方法是用C++或者C之类的语言编译的,运行起来比必须实时编译的javascript要高效的多。比如你可能像要自己编写一个求阶乘的函数,但是实际上你应该使用javascript内置的Math.pow()方法。

11、存储常用的值
当多次使用到一个值得时候,可先将其存储在局部变量中以便快速访问。尤其对于通常使用对象的特性来进行访问的值更加重要。如:

oDiv1.style.left = document.body.clientWidth;
oDiv2.style.left = document.body.clientWidth;document.body.clientWidth在该例中被使用了两次,但它是使用命名特性(属于极其昂贵的操作)来获取的。可以使用局部变量来重写这段代码:

var iClientWidth = document.body.clientWidth;
oDiv1.style.left = iClientWidth;
oDiv2.style.left = iClientWidth;12、最小化语句数量
我们有理由相信,脚本中语句越少,执行所需要的时间越短。很多方法可以将javascript中的语句数量减到最少,比如定义变量时,处理迭代数字时,使用数组和对象字面量时。

多个变量的定义可用一个var语句来替代:

var iFive = 5, sColor = "red", aValues = [1,2,3], Date = new Date();插入迭代子

使用迭代子(在不同位置上加减的值)时,尽可能合并语句。考虑下面代码:

var sName = aValues[i];
i++;这样写更简短:

var sName = aValues[i++];13、节约使用DOM
不管是添加、删除或者是其他对页面DOM内部结构的更改,都会导致整个页面的重新渲染,带来的是明显的时间消耗。解决这个问题的方法是尽可能地对不在DOM文档中的元素节点进行操作。如下例子:

var UL = document.getElementById("ulItems");
for(var i=0; i<10; i++){
 var LI = document.createElement("li");
 oUL.appendChild(oLI);    //I
 oLI.appendChild(document.createTextNode("Item "+i));    //II
}每次执行I都会对整个页面重新渲染一次,执行II又会对整个页面重新渲染一次,总共会有20次的对页面的渲染。我们大可使用文档碎片来保存所有列表项,最后再一起添加到文档中。

var UL = document.getElementById("ulItems");
var Fragment = document.createDocumentFragment();
for(var i=0; i<10; i++){
 var LI = document.createElement("li");
 oLI.appendChild(document.createTextNode("Item "+i));
 oFragment.appendChild(oLI);
}
oUL.appendChild(oFragment);这样对文档中DOM树的操作就只有一次。

其他一些优化方法

1、不要使用eval
使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间。这时候使用JavaScript所支持的闭包可以实现函数模版(关于闭包的内容请参考函数式编程的有关内容)

2、类型转换
类型转换是大家常犯的错误,因为JavaScript是动态类型语言,你不能指定变量的类型。

把数字转换成字符串,应用”" + 1,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:(”" + ) > String() > .toString() > new String()这条其实和下面的“直接量”有点类似,尽量使用编译时就能使用的内部操作要比运行时使用的用户操作要快。String()属于内部函数, 所以速度很快,而.toString()要查询原型中的函数,所以速度逊色一些,new String()用于返回一个精确的副本。
浮点数转换成整型,这个更容易出错,很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数 和整型之间的转换,我们应该使用Math.floor()或者Math.round()。另外,和第二节的对象查找中的问题不一样,Math是内部对象, 所以Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。
对于自定义的对象,如果定义了toString()方法来进行类型转换的话,推荐显式调用toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高
3、字符串遍历操作
对字符串进行循环操作,譬如替换、查找,应使用正则表达式,因为本身JavaScript的循环速度就比较慢,而正则表达式的操作是用C写成的语言的API,性能很好。

4、DOM相关

插入HTML 很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容 器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。
创建节点 尽量避免只是使用html字符串来创建节点。因为这样做既无法保证代码的有效性,同时字符串的操作效率有极低。所以应该是用 document.createElement()方法,而如果文档中存在现成的样板节点,应该是用cloneNode()方法,因为使用 createElement()方法之后,你需要设置多次元素的属性,使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元 素,应该先准备一个样板节点。
定时器 如果针对的是不断运行的代码,不应该使用setTimeout,而应该是用setInterval。setTimeout每次要重新设置一个定时器





jsp速度优化七种方法

文章分类:Java编程调整JSP和servlet的一些非常实用的方法,它可使你的servlet和JSP页面响应更快,扩展性更强。而且在用户数增加的情况下,系统负载会呈 现出平滑上长的趋势。在本文中,我将通过一些实际例子和配置方法使得你的应用程序的性能有出人意料的提升。其中,某些调优技术是在你的编程工作中实现的。 而另一些技术是与应用服务器的配置相关的。在本文中,我们将详细地描述怎样通过调整servlet和JSP页面,来提高你的应用程序的总体性能。在阅读本 文之前,假设你有基本的servlet和JSP的知识。



方法一:在servlet的init()方法中缓存数据


当应用服务器初始化servlet实例之后,为客户端请求提供服务之前,它会调用这个servlet的init()方法。在一个servlet的 生命周期中,init()方法只会被调用一次。通过在init()方法中缓存一些静态的数据或完成一些只需要执行一次的、耗时的操作,就可大大地提高系统 性能。 大学生校园网(www.vvschool.cn)

例如,通过在init()方法中建立一个JDBC连接池是一个最佳例子,假设我们是用jdbc2.0的DataSource接口来取得数据库连 接,在通常的情况下,我们需要通过JNDI来取得具体的数据源。我们可以想象在一个具体的应用中,如果每次SQL请求都要执行一次JNDI查询的话,那系 统性能将会急剧下降。解决方法是如下代码,它通过缓存DataSource,使得下一次SQL调用时仍然可以继续利用它: public class ControllerServlet extends HttpServlet {  private javax.sql.DataSource testDS = null;  public void init(ServletConfig config) throws ServletException  { super.init(config); Context ctx = null; try {  ctx = new InitialContext();  testDS = (javax.sql.DataSource)ctx.lookup("jdbc/testDS"); } catch(NamingException ne) {  ne.printStackTrace(); } catch(Exception e) {  e.printStackTrace(); }  }  public javax.sql.DataSource getTestDS()  { return testDS;  }  ...  ... }

方法 2:禁止servlet和JSP 自动重载(auto-reloading)
Servlet/JSP提供了一个实用的技术,即自动重载技术,它为开发人员提供了一个好的开发环境,当你改变servlet和JSP页面后而不 必重启应用服务器。然而,这种技术在产品运行阶段对系统的资源是一个极大的损耗,因为它会给JSP引擎的类装载器(classloader)带来极大的负 担。因此关闭自动重载功能对系统性能的提升是一个极大的帮助。


方法 3: 不要滥用HttpSession

在很多应用中,我们的程序需要保持客户端的状态,以便页面之间可以相互联系。但不幸的是由于HTTP具有天生无状态性,从而无法保存客户端的状 态。因此一般的应用服务器都提供了session来保存客户的状态。在JSP应用服务器中,是通过HttpSession对像来实现session的功能 的,但在方便的同时,它也给系统带来了不小的负担。因为每当你获得或更新session时,系统者要对它进行费时的序列化操作。你可以通过对 HttpSession的以下几种处理方式来提升系统的性能:



如果没有必要,就应该关闭JSP页面中对HttpSession的缺省设置: 如果你没有明确指定的话,每个JSP页面都会缺省地创建一个HttpSession。如果你的JSP中不需要使用session的话,那可以通过如下的 JSP页面指示符来禁止它: <%@ page session="false"%>



不要在HttpSession中存放大的数据对像:如果你在HttpSession中存放大的数据对像的话,每当对它进行读写时,应用服务器都将对其进行序列化,从而增加了系统的额外负担。你在HttpSession中存放的数据对像越大,那系统的性能就下降得越快。

当你不需要HttpSession时,尽快地释放它:当你不再需要session时,你可以通过调用HttpSession.invalidate()方法来释放它。



尽量将session的超时时间设得短一点:在JSP应用服务器中,有一个缺省的session的超时时间。当客户在这个时间之后没有进行任何操 作的话,系统会将相关的session自动从内存中释放。超时时间设得越大,系统的性能就会越低,因此最好的方法就是尽量使得它的值保持在一个较低的水 平。

方法 4: 将页面输出进行压缩

压缩是解决数据冗余的一个好的方法,特别是在网络带宽不够发达的今天。有的浏览器支持gzip(GNU zip)进行来对HTML文件进行压缩,这种方法可以戏剧性地减少HTML文件的下载时间。因此,如果你将servlet或JSP页面生成的HTML页面 进行压缩的话,那用户就会觉得页面浏览速度会非常快。但不幸的是,不是所有的浏览器都支持gzip压缩,但你可以通过在你的程序中检查客户的浏览器是否支 持它。下面就是关于这种方法实现的一个代码片段:


public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {  OutputStream ut = null  String encoding = request.getHeader("Accept-Encoding");  if (encoding != null && encoding.indexOf("gzip") != -1)  { request.setHeader("Content-Encoding" , "gzip"); ut = new GZIPOutputStream(request.getOutputStream());  }  else if (encoding != null && encoding.indexOf("compress") != -1)  { request.setHeader("Content-Encoding" , "compress"); ut = new ZIPOutputStream(request.getOutputStream());  }  else  { ut = request.getOutputStream();  }  ...  ... }


方法 5: 使用线程池

应用服务器缺省地为每个不同的客户端请求创建一个线程进行处理,并为它们分派service()方法,当service()方法调用完成后,与之 相应的线程也随之撤消。由于创建和撤消线程会耗费一定的系统资源,这种缺省模式降低了系统的性能。但所幸的是我们可以通过创建一个线程池来改变这种状况。 另外,我们还要为这个线程池设置一个最小线程数和一个最大线程数。在应用服务器启动时,它会创建数量等于最小线程数的一个线程池,当客户有请求时,相应地 从池从取出一个线程来进行处理,当处理完成后,再将线程重新放入到池中。如果池中的线程不够地话,系统会自动地增加池中线程的数量,但总量不能超过最大线 程数。通过使用线程池,当客户端请求急剧增加时,系统的负载就会呈现的平滑的上升曲线,从而提高的系统的可伸缩性。

方法 6: 选择正确的页面包含机制
在JSP中有两种方法可以用来包含另一个页面1、使用include指示符(<%@ includee file=”test.jsp” %>)。2、使用jsp指示符(<jsp:includee page=”test.jsp” flush=”true”/>)。在实际中我发现,如果使用第一种方法的话,可以使得系统性能更高。

方法 7:正确地确定javabean的生命周期

JSP的一个强大的地方就是对javabean的支持。通过在JSP页面中使用<jsp:useBean>标签,可以将javabean直接插入 到一个JSP页面中。它的使用方法如下: <jsp:useBean id="name" scope="page|request|session|application" class= "package.className" type="typeName"></jsp:useBean> 

其中scope属性指出了这个bean的生命周期。缺省的生命周期为page。如果你没有正确地选择bean的生命周期的话,它将影响系统的性能。



举例来说,如果你只想在一次请求中使用某个bean,但你却将这个bean的生命周期设置成了session,那当这次请求结束后,这个bean 将仍然保留在内存中,除非session超时或用户关闭浏览器。这样会耗费一定的内存,并无谓的增加了JVM垃圾收集器的工作量。因此为bean设置正确 的生命周期,并在bean的使命结束后尽快地清理它们,会使用系统性能有一个提高。

其它一些有用的方法 在字符串连接操作中尽量不使用“+”操作符:在java编程中,我们常常使用“+”操作符来将几个字符串连接起来,但你或许从来没有想到过它居然会对系统 性能造成影响吧?由于字符串是常量,因此JVM会产生一些临时的对像。你使用的“+”越多,生成的临时对像就越多,这样也会给系统性能带来一些影响。解决 的方法是用StringBuffer对像来代替“+”操作符。

避免使用System.out.println()方法:由于System.out.println()是一种同步调用,即在调用它时,磁盘 I/O操作必须等待它的完成,因此我们要尽量避免对它的调用。但我们在调试程序时它又是一个必不可少的方便工具,为了解决这个矛盾,我建议你最好使用 Log4j工具(http://Jakarta.apache.org ; ),它既可以方便调试,而不会产生System.out.println()这样的方法。

ServletOutputStream 与 PrintWriter的权衡:使用PrintWriter可能会带来一些小的开销,因为它将所有的原始输出都转换为字符流来输出,因此如果使用它来作为 页面输出的话,系统要负担一个转换过程。而使用ServletOutputStream作为页面输出的话就不存在一个问题,但它是以二进制进行输出的。因 此在实际应用中要权衡两者的利弊。




一、代码优化. 检查点如下:

1、整个jsp/jsp bean表示层应当尽可能的瘦和简单化。

2、牢记大多数的JSP都应当是只读的视图,而由页面bean来提供模型。

3、应当一起设计JSP和JSP bean

4、应当在JSP中避免使用页面引入指令。import指令会促使类的实列化而不是jsp

5、bean的实例化

6、在JSP中避免代码重复。把要重复的功能放在一个包含的JSP、bean或标记扩展中,使得它能够被重用。

7、在JSP中应该避免使用out.println()发放来产生页面内容。

8、除了JSP bean之外,JSP不应当去实例化复杂的可读写的对象。如果这样的话,就有可能在JSP中去执行不适当的业务逻辑。

9、应当使用隐藏的注释来阻止输出的HTML过大。

10、如果没有充分的理由,就不要在JSP中定义方法和内部类。

11、在Servlet/JSP中尽量避免使用实例变量

12、应当在JSP中避免使用页面引入指令。import指令会促使类的实列化而不是jsp bean的实例化不用:<%@ page import = "com.java.util.*" %> 而用:<% java.util.List l = new java.util.LinkedList(); %>

13、JSP不应当访问属性文件,或者使用JNDI。bean可以访问属性文件。

14、JSP bean应当永远不要去产生HTML。

15、JSP bean中不应当包含大量的数据。

16、定制标记应当用在适当把逻辑从jsp中移走的地方。

17、"""返回上一级""或“后退”的链接不宜写成:返回上一级使用prev指令回退并不一定能达到真正回退的效果,会出现不正常跳转。"

18、建议不要硬编码,特别是页面链接、参数、文字信息,如果写在java文件中,会带来很大的维护工作量。我们的目的是将现网操作尽量简化,尽量减少。尽量用配置文件、数据库表来实现

二、业务安全

1、在尽可能合理的情况下,把业务逻辑从JSP中移走。具体于HTTP的逻辑(如,对Cookie的处理)属于bean或支持类中,而不是JSP中。

2、JSP层不应该直接访问数据,这包括JDBC数据库访问和EJB访问。

3、JSP不应该直接去访问请求参数。bean应当执行这样的处理过程并且输出所处理的模型数据。

4、尽量把条件逻辑放在控制器中而不是放在视图中。

三、结构

1、每个JSP文件中都应当使用一个错误页面来处理不能够从中恢复的异常。

2、在JSP中避免进行异常处理

【编辑推荐】

  1. Servlet和JSP中的重定向技术的综述
  2. JSP连接ORACLE数据库时注意的一些问题
  3. 简单介绍JSP数据库高级操作
  4. Jsp输出excel文档和中文乱码问题的解决
  5. 如何解决JSP页面显示乱码问题


TAG:

 

评分:0

我来说两句

Open Toolbar