(3). 避免使用eval和Function
每次 eval 或 Function 构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操作 —— 通常比简单的函数调用慢100倍以上。
eval 函数效率特别低,由于事先无法知晓传给 eval 的字符串中的内容,eval在其上下文中解释要处理的代码,也就是说编译器无法优化上下文,因此只能有浏览器在运行时解释代码。这对性能影响很大。
Function 构造函数比eval略好,因为使用此代码不会影响周围代码;但其速度仍很慢。
此外,使用eval和Function也不利于Javascript压缩工具执行压缩。
(4). 减少作用域链查找
前文谈到了作用域链查找问题,这一点在循环中是尤其需要注意的问题。如果在循环中需要访问非本作用域下的变量时请在遍历之前用局部变量缓存该变量,并在遍历结束后再重写那个变量,这一点对全局变量尤其重要,因为全局变量处于作用域链的最顶端,访问时的查找次数是最多的。
低效率的写法:
//全局变量 var globalVar = 1; function myCallback(info){ for( var i = 100000; i--;){ //每次访问globalVar都需要查找到作用域链最顶端,本例中需要访问100000次 globalVar += i; } } |
更高效的写法:
//全局变量 var globalVar = 1; function myCallback(info){ //局部变量缓存全局变量 var localVar = globalVar; for( var i = 100000; i--;){ //访问局部变量是最快的 localVar += i; } //本例中只需要访问2次全局变量 globalVar = localVar; } |
此外,要减少作用域链查找还应该减少闭包的使用。
(5). 数据访问
Javascript中的数据访问包括直接量(字符串、正则表达式)、变量、对象属性以及数组,其中对直接量和局部变量的访问是最快的,对对象属性以及数组的访问需要更大的开销。当出现以下情况时,建议将数据放入局部变量:
a. 对任何对象属性的访问超过1次
b. 对任何数组成员的访问次数超过1次
另外,还应当尽可能的减少对对象以及数组深度查找。
(6). 字符串拼接
在Javascript中使用"+"号来拼接字符串效率是比较低的,因为每次运行都会开辟新的内存并生成新的字符串变量,然后将拼接结果赋值给新变量。与之相比更为高效的做法是使用数组的join方法,即将需要拼接的字符串放在数组中最后调用其join方法得到结果。不过由于使用数组也有一定的开销,因此当需要拼接的字符串较多的时候可以考虑用此方法。
关于Javascript优化的更详细介绍请参考:
2. CSS选择符
在大多数人的观念中,都觉得浏览器对CSS选择符的解析式从左往右进行的,例如
#toc A { color: #444; } |
这样一个选择符,如果是从右往左解析则效率会很高,因为第一个ID选择基本上就把查找的范围限定了,但实际上浏览器对选择符的解析是从右往左进行的。如上面的选择符,浏览器必须遍历查找每一个A标签的祖先节点,效率并不像之前想象的那样高。根据浏览器的这一行为特点,在写选择符的时候需要注意很多事项,有人已经一一列举了,。
3. HTML
对HTML本身的优化现如今也越来越多的受人关注了,详情可以参见这篇。
4. Image压缩
图片压缩是个技术活,不过现如今这方面的工具也非常多,压缩之后往往能带来不错的效果,具体的压缩原理以及方法在《Even Faster Web Sites》第10章有很详细的介绍,有兴趣的可以去看看。
总结
本文从页面级以及代码级两个粒度对前端优化的各种方式做了一个总结,这些方法基本上都是前端开发人员在开发的过程中可以借鉴和实践的,除此之外,完整的前端优化还应该包括很多其他的途径,例如CDN、Gzip、多域名、无Cookie服务器等等,由于对于开发人员的可操作性并不强大,在此也就不多叙述了,详细的可以参考Yahoo和Google的这些“金科玉律”。