javascript性能优化总结 —— 编程习惯篇
2013-05-03 21:00 MoltBoy 阅读(2014) 评论(4) 编辑 收藏 举报在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; } }
看看上诉代码,有哪些地方需要优化的?这里提醒一句,切忌过早的优化,这会让你束手束脚,并且极度不利于后续的开发,成为万恶之源!
使用local变量
上面的代码中,有两个地方使用到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对于循环优化也有大量研究。例如:
- 减值迭代,大多数的情况下,我们都喜欢从0开始,然后自增进行迭代控制。而事实上,进行减值迭代,可以提高35%的性能(firefox测试结果)。也就是使用最大值,然后进行自减迭代控制;
- 简化判断条件,这中间有比较多的技巧,例如:利用变量自增或自减,当等于0时退出循环;while(i),就没必要写出while(i < 10)之类的情况;另外,就是NodeList对象的length属性的访问,在迭代中循环访问属性length也比较消耗资源,良好习惯是用移出循环,或者循环定义语句中储存局部变量中;
- 简化循环主体,这是重点,一定要确保最大限度地优化,确保没有任何可以被移出循环的计算或者变量定义等。计算或者没必要的语句,大部分开发人员都懂得移出,但是经常会看见变量的定义出现在循环体中,除非必要情况,否则也要移出到循环之外;
- 使用后判断循环,for和while都是前判断循环,do-while是后判断循环,因为可以避免最初终止条件的运算,所以会相对快点。
var i = 10; do{ console.log(i); }while(i--);
其他需要注意事项:
- 使用原生方法,因为这些方法都是C/C++之类的编译语言编写,所以一般都要比javascript的快。
- 使用Switch语句,比使用一系列if-else语句要快出许多,另外,case语句可以按照最多可能到达的到最少可能到达的顺序组织。
- 避免使用双重解释,例如:eval("alert('Hello')");setTimeout("alert('hello')", 500);因为初次解释过程中,不能解释字符串中的语句,因此需要另外实例化一个解释器来进行解 释,自然会降低性能。
- 谨慎地使用闭包,毕竟对内存资源消耗太大。
- 修改元素样式,尽可能使用添加css class或者替换css class方式.
DOM交互的性能优化
DOM交互是javascript中最消耗资源的操作。一次小小的操作,都会导致页面回流,浏览器需要重新计算无数尺寸进行相应的更新。所以,DOM交互性能优化的基本原则就是:尽可能减少DOM操作次数。
当遇上需要使用DOM对页面进行更新时,推荐使用文档碎片(documentFragment)来构建DOM结构,最后一次性或者少次添加到DOM文档中去。当然,能使用innerHTML属性时,义不容辞绝对优先使用innerHTML,这个地方千万别客气!因为,当使用innerHTML设置属性值时,后台会自动创建一个HTML解释器,然后使用内部的DOM方法创建DOM结构,而不是基于javascript的DOM方法。因为内部方法是编译好的,因此执行比较快。
使用事件冒泡机制,任何可以冒泡的事件不仅可以在目标元素上处理,也可以在目标元素的祖先节点进行处理。因此,可以使用冒泡机制在更高层进行事件处理,如此可以负责多个目标事件的处理。
其他优化
至于,压缩及其部署过程中的优化,可以参加yahoo的14条优化原则进行优化。写的很不全面,因此,极力推荐精读Steve Souders的两本巨作,后面一本是合著。