<高性能javascript>每章总结

2012年3月20日

1、加载和运行

管理浏览器中的javascript代码是个棘手的问题,因为代码执行阻塞了其他浏览器处理过程,诸如用户界面绘制。每次遇到script标签,页面必须停下来等待代码下载(如果是外部的)并执行,然后再继续处理页面其他部分。但是,有几种方法可以减少javascript对性能的影响:
将所有script标签放置在页面的底部,紧靠body关闭标签的上方。此法可以保证页面在脚本运行之前完成解析。
将脚本成组打包。页面的script标签越少,页面的加载速度就越快,响应也更加迅速。不论外部脚本文件还是内联代码都是如此。
有几种方法可以使用非阻塞方式下载javascript:
为script标签添加defer属性(只适用于internet explorer 和 firefox 3.5以上版本)
动态创建script元素,用它下载并执行代码
用XHR对象下载代码,并注入到页面中

2、数据访问

在javascript中,数据存储位置可以对代码整体性能产生重要影响。有四种数据访问类型:直接量,变量,数组项,对象成员。它们有不同的性能考虑。
直接量和局部变量访问速度非常快,数组项和对象成员需要更长时间。
局部变量比域外变量快,因为它位于作用域链的第一个对象中。变量在作用域链中的位置越深,访问所需的时间就越长。全局变量总是最慢的,因为它们总是位于作用域链的最后一环。
避免使用with表达式,因为它改变了运行期上下文的作用域链。而且应当小心对待try-catch表达式的catch子句,因为它具有同样效果。
嵌套对象成员会造成重大性能影响,尽量少用。
一个属性或方法在原型链中的位置越深,访问它的速度就越慢。
一般来说,你可以通过这种方法提高javascript代码的性能:将经常使用的对象成员,数组项,和域外变量存入局部变量中。然后,访问局部变量的速度会快于那些原始变量。

3、DOM编程

DOM访问和操作是现代网页应用中很重要的一部分。但每次你通过桥梁从ECMAScript岛到达DOM岛时,都会被收取“过桥费”。为减少DOM编程中的性能损失,请牢记以下几点:
最小化DOM访问,在javascript端做尽可能多的事情。
在反复访问的地方使用局部变量存放DOM引用。
小心地处理HTML集合,因为他们表现出“存在性”,总是对底层文档重新查询。将集合的length属性缓存到一个变量中,在迭代中使用这个变量。如果经常操作这个集合,可以将集合拷贝到数组中。
如果可能的话,使用速度最快的API,诸如querySelectorAll()和firstElementChild。
注意重绘和重排版;批量修改风格,离线操作DOM树,缓存并减少对局部信息的访问。
动画中使用绝对坐标,使用拖放代理。
使用事件委托技术最小化事件句柄数量。

4、算法和流程控制

正如其他编程语言,代码的写法和算法选用影响javascript的运行时间。与其他编程语言不通的是,javascript可用资源有限,所以技术更为重要。
for,while,do-while循环的性能特性相似,谁也不比谁更快或更慢。
除非你要迭代遍历一个属性未知的对象,否则不要使用for-in循环。
改善循环性能的最好办法是减少每次迭代中的运算量,并减少循环迭代次数。
一般来说,switch总是比if-else更快,但并不总是最好的解决办法。
当判断条件较多时,查表法比if-else或者switch更快。
浏览器的调用栈尺寸限制了递归算法在javascript中的应用:栈溢出错误导致其他代码也不能正常执行。
如果你遇到一个栈溢出错误,将方法修改为一个迭代算法或者制表法可以避免重复工作。
运行的代码总量越大,使用这些策略所带来的性能提升就越明显。

5、字符串和正则表达式

密集的字符串操作和粗浅地编写正则表达式可能是主要性能障碍,但如下建议可帮助您避免常见缺陷。
当连接数量巨大或尺寸巨大的字符串时,数组联合是IE7和它早期版本上唯一具有合理性能的方法。
如果你不关心IE7和它的早期版本,数组联合是连接字符串最慢的方法之一。使用那个简单的+和+=取而代之,可避免(产生)不必要的中间字符串。
回溯即是正则表达式匹配功能的组成部分,又是正则表达式影响效率的常见原因。
回溯失控发生在正则表达式本应很快发现匹配的地方,因为某些特殊的匹配字符串动作,导致运行缓慢甚至浏览器崩溃。避免此问题的技术包括:使相邻字元互斥,避免桥套量词对一个字符串的相同部分多次匹配,通过重复利用前瞻操作的原子特性去除不必要的回溯。
提高正则表达式效率的各种技术手段,帮助正则表达式更快地找到匹配,以及在非匹配位置上花费更少时间。
正则表达式并不总是完成工作的最佳工具,尤其当你只是搜索一个文本字符串时。
虽然有很多方法来修整一个字符串,使用两个简单的正则表达式(一个用于去除头部空格,另一个用于去除尾部空格)提供了一个简洁、跨浏览器的方法,使用与不同内容长度的字符串。从字符串末尾开始循环查找第一个非空格字符,或者在一个混合应用中将此技术与正则表达式结合起来,提供了一个很好的替代方法,它很少受到字符串整体长度的影响。

6、响应接口

javascript和用户界面更新在同一个进程内运行,同一时刻只有其中一个可以运行。这意味着当javascript代码正在运行时,用户界面不能响应输入,反之亦然。有效地管理UI线程就是要确保javascript不能运行太长时间,以免影响用户体验。最后,请牢记如下几点:
javascript运行时间不应该超过100毫秒。过长的运行时间导致UI更新出现可察觉的延迟,从而对整体用户体验产生负面影响。
javascript运行期间,浏览器响应用户交互的行为存在差异。无论如何,javascript长时间运行将导致用户体验混乱和脱节。
定时器可用于安排代码推迟执行,它使得你可以将长运行脚本分解成一系列较小的任务。
网页工人线程是新式浏览器才支持的特性,它允许你在UI线程之外运行javascript代码而避免锁定UI。
网页应用程序越复杂,积极主动的管理UI线程就越显得重要。没有什么javascript代码可以重要到允许影响用户体验的程度。

7、ajax异步javascript和XML

高性能ajax包括:知道你项目的具体需求,选择正确的数据格式和与之相配的传输技术。作为数据格式,纯文本和HTML是高度限制的,但它们可节省客户端的CPU周期。XML被广泛应用普遍支持,但它非常冗长且解析缓慢。JSON是轻量级的,解析迅速(作为本地代码而不是字符串),交互性与XML相当。字符分隔的自定义格式非常轻量,在大量数据集解析时速度最快,但需要编写额外的程序在服务器端构造格式,并在客户端解析。当从页面域请求数据时,XHR提供最完善的控制和灵活性,尽管它将所有传入数据视为一个字符串,这有可能降低解析速度。另一方面,动态脚本标签插入技术运行允许跨域请求和本地运行javascript和json,虽然它的接口不够安全,而且不能读取信息头或响应报文代码。多部分XHR可减少请求的数量,可在一次响应中处理不同的文件类型,尽管它不能缓存受到的响应报文。当发送数据时,图像灯标是最简单和最有效的方法。XHR也可用POST方法发送大量数据。除了这些格式和传输技术之外,还有一些准则有助于进一步提高ajax的速度:
减少请求数量,可通过javascript和css文件打包,或者使用MXHR。
缩短页面的加载时间,在页面其它内容加载之后,使用ajax获取少量重要文件。
确保代码错误不要直接显示给用户,并在服务器端处理错误。
学会何时使用一个健壮的ajax库,何时编写自己的底层ajax代码。
ajax是提升你网站潜在性能最大的改进区域之一,因为很多网站大量使用异步请求,又因为它提供了许多不相关问题的解决方案,这些问题诸如,需要加载太多资源。对XHR的创造性应用是如此的与众不同,它不是呆滞不友好的界面,而是响应迅速且高效的代名词;它不会引起用户的憎恨,谁见了它都会爱上它。

8、编程实践

javascript提出了一些独特的性能挑战,关系到你组织代码的方法。网页应用变得越来越高级,包含的javascript代码越来越多,出现了一些模式和反模式。请牢记以下编程经验:
通过避免使用eval_r()和Function()构造器避免二次评估。此外,给setTimeout()和setInterval()传递函数参数而不是字符串参数。
创建新对象数组时使用对象直接量和数组直接量。它们比非直接量形式创建和初始化更快。
避免重复进行相同工作。当需要检测浏览器时,使用延迟加载或条件预加载。
当执行数学运算时,考虑使用位操作,它直接在数字底层进行操作。
原生方法总是比javascript写的东西要快。尽量使用原生方法。

9、创建并部署高性能javascript应用程序

开发和部署成果对基于javascript的应用程序可以产生巨大影响,最重要的几个步骤如下:
合并javascript文件,减少HTTP请求的数量
使用YUI压缩器紧凑处理javascript文件
以压缩形式提供javascript文件(gzip编码)
通过设置HTTP响应报文头使javascript文件可缓存,通过向文件名附加时间戳解决缓存问题
使用内容传递网络(CDN)提供javascript文件,CDN不仅可以提高性能,它还可以为你管理压缩和缓存
所有这些步骤应当自动完成,不论是使用公开的开发工具诸如Apache Ant,还是使用自定义的开发工具以实现特定需求。如果你使这些开发工具为你服务,你可以极大改善那些大量使用javascript代码的网页应用或网站性能。