javascript性能优化总结

在google,网页呈现速度慢500毫秒将丢失20%的流量;在yahoo!,慢上400毫秒将丢失5%-9%的流量;在亚马逊(Amazon),慢上100毫秒将丢失1%的交易量…这是速度绝对成败的web时代,天下武学唯快不破,相信没人嫌弃网页打开速度太快吧!

那么要让网页速度更快,我们应该如何优化,应该保持怎样的编程习惯,让代码运行的更加流畅?在这里,郑重推荐《高性能网站建设指南》和《高性能网站建设进阶指南》,完全掌握这两本书的内容对你的前端生涯将是受益匪浅。

言归正传,javascript是解释型语言,执行速度自然比不上编译型语言,另外,计算机系统分配给浏览器的资源有限,分给web应用的那就更少。因此,javascript程序性能比不上其他编译型语言,然而,2005年之后,浏览器开发商对javascript执行性能进行了大量优化,javascript的性能已经是大步前进。不过,仍然有许多地方可以提高整体代码的性能。

function updateUI(){
    var divs = document.getElementsByTagName("div");
    for(var i = 0; i < divs.length; i++){
        divs[i].innerHTML = document.title + " div " + i;
    }                    
}

看看上诉代码,有哪些地方需要优化的?这里提醒一句,切忌过早的优化,这会让你束手束脚,并且极度不利于后续的开发,成为万恶之源!

上面的代码中,有两个地方使用到document全局对象,而访问全局对象需要遍历整个作用域链,自然比访问局部变量要开销大,特别是上述代码中在迭代中访问。因此多次访问全局对象时,尽可能使用局部变量指向全局对象。如此,只需要一次遍历整个作用域链,后面的复杂度为O(1)。总之,将需要多次访问的全局对象储存在局部变量中准没错。

刚刚提到了复杂度,这里就补充算法的复杂度,并无深入研究,但绝对少不了。最快速的算法常数值表示为O(1),另外还有O(log n)、O(n)、O(n2)、O(n3),分别为对数、线性、平方、立方,依次复杂度越来越高,其中n表示值的数量。

常量值的获取是复杂度为O(1),也即是说无论获取多少个常量值,时间都是一样的。

而对象的访问获取复杂度为O(n),也就是访问时间随数量值的增加而增加,成线性规律。相比而言,数组的访问要快些,因为对象访问必须在原型链中对该名称的属性进行一次遍历。在同样可以使用索引数字或者属性名称的对象中,使用索引访问更快捷,当然优先使用属性专用访问方式(.)。

function updateUI(){
    var i = divs.length,
        doc  = document,
        divs = doc.getElementsByTagName("div");
    for(; i ; i--){
      divs[i-1].innerHTML = doc.title + " div " + (i - 1);          
    }
}

上诉代码为性能牺牲了代码易读性,所以性能优化和代码优雅以及易读性等各方面需要综合考虑,找出适合需求的方案才是最佳解决方案。

循环是性能优化中非常重要的地方,像其他编程语言一样,javascript对于循环优化也有大量研究。例如:

  1. 减值迭代,大多数的情况下,我们都喜欢从0开始,然后自增进行迭代控制。而事实上,进行减值迭代,可以提高35%的性能(firefox测试结果)。也就是使用最大值,然后进行自减迭代控制;
  2. 简化判断条件,这中间有比较多的技巧,例如:利用变量自增或自减,当等于0时退出循环;while(i),就没必要写出while(i < 10)之类的情况;另外,就是NodeList对象的length属性的访问,在迭代中循环访问属性length也比较消耗资源,良好习惯是用移出循环,或者循环定义语句中储存局部变量中;
  3. 简化循环主体,这是重点,一定要确保最大限度地优化,确保没有任何可以被移出循环的计算或者变量定义等。计算或者没必要的语句,大部分开发人员都懂得移出,但是经常会看见变量的定义出现在循环体中,除非必要情况,否则也要移出到循环之外;
  4. 使用后判断循环,for和while都是前判断循环,do-while是后判断循环,因为可以避免最初终止条件的运算,所以会相对快点。

其他需要注意事项:

  •  使用原生方法,因为这些方法都是C/C++之类的编译语言编写,所以一般都要比javascript的快。
  •    使用Switch语句,比使用一系列if-else语句要快出许多,另外,case语句可以按照最多可能到达的到最少可能到达的顺序组织。
  •    避免使用双重解释,例如:eval(“alert(‘Hello’)”);setTimeout(“alert(‘hello’)”, 500);因为初次解释过程中,不能解释字符串中的语句,因此需要另外实例化一个解释器来进行解      释,自然会降低性能。

DOM交互的性能优化

DOM交互是javascript中最消耗资源的操作。一次小小的操作,都会导致页面回流,浏览器需要重新计算无数尺寸进行相应的更新。所以,DOM交互性能优化的基本原则就是:尽可能减少DOM操作次数。

当遇上需要使用DOM对页面进行更新时,推荐使用文档碎片(documentFragment)来构建DOM结构,最后一次性或者少次添加到DOM文档中去。当然,能使用innerHTML属性时,义不容辞绝对优先使用innerHTML,这个地方千万别客气!因为,当使用innerHTML设置属性值时,后台会自动创建一个HTML解释器,然后使用内部的DOM方法创建DOM结构,而不是基于javascript的DOM方法。因为内部方法是编译好的,因此执行比较快。

使用事件冒泡机制,任何可以冒泡的事件不仅可以在目标元素上处理,也可以在目标元素的祖先节点进行处理。因此,可以使用冒泡机制在更高层进行事件处理,如此可以负责多个目标事件的处理。

至于,压缩及其部署过程中的优化,可以参加yahoo的14条优化原则进行优化。写的很不全面,因此,极力推荐精读Steve Souders的两本巨作,后面一本是合著。

标签