Chapter 8: 实践技巧
避免双重执行
这个部分讨论的是动态执行的JS代码,通过eval(),Function(),setTimeout()和setInterval()所创建的代码都会经过两次执行的过程,而这导致它们慢很多。
作者的建议是,对于eval()和Function()最好是索性直接避免,而后两者setTimeout()和setInterval(),可以传递预先定义的函数。
====================================================================
使用字面定义
这个问题至少David在【Effective JavaScript】里说过。简单讲就是:
//create an object
var myObject = new Object();
myObject.name = "Nicholas";
myObject.count = 50;
myObject.flag = true;
myObject.pointer = null;
//create an array
var myArray = new Array();
myArray[0] = "Nicholas";
myArray[1] = 50;
myArray[2] = true;
myArray[3] = null;
不如下面执行效率高:
//create an object
var myObject = {
name: & quot;Nicholas & quot;,
count: 50,
flag: true,
pointer: null
};
//create an array
var myArray = [ & quot;Nicholas & quot;, 50, true, null];
====================================================================
不要重复执行做过的事情
这个部分的例子很经典,判断浏览器和版本,决定执行怎样的代码,通常的写法可能会是:
function addHandler(target, eventType, handler) {
if (target.addEventListener) { //DOM2 Events
target.addEventListener(eventType, handler, false);
} else { //IE
target.attachEvent("on" + eventType, handler);
}
}
function removeHandler(target, eventType, handler) {
if (target.removeEventListener) { //DOM2 Events
target.removeEventListener(eventType, handler, false);
} else { //IE
target.detachEvent("on" + eventType, handler);
}
}
这里判断浏览器的逻辑被重复了。有两个解决方案:
慵懒加载
function addHandler(target, eventType, handler) {
//overwrite the existing function
if (target.addEventListener) { //DOM2 Events
addHandler = function(target, eventType, handler) {
target.addEventListener(eventType, handler, false);
};
} else { //IE
addHandler = function(target, eventType, handler) {
target.attachEvent("on" + eventType, handler);
};
}
//call the new function
addHandler(target, eventType, handler);
}
function removeHandler(target, eventType, handler) {
//overwrite the existing function
if (target.removeEventListener) { //DOM2 Events
removeHandler = function(target, eventType, handler) {
target.addEventListener(eventType, handler, false);
};
} else { //IE
removeHandler = function(target, eventType, handler) {
target.detachEvent("on" + eventType, handler);
};
}
//call the new function
removeHandler(target, eventType, handler);
}
在一个函数体自身内重新定义该函数,厉害呀!适用于并非在页面加载完成后马上要执行的功能。
条件性预先加载
var addHandler = document.body.addEventListener ?
function(target, eventType, handler) {
target.addEventListener(eventType, handler, false);
} :
function(target, eventType, handler) {
target.attachEvent("on" + eventType, handler);
};
var removeHandler = document.body.removeEventListener ?
function(target, eventType, handler) {
target.removeEventListener(eventType, handler, false);
} :
function(target, eventType, handler) {
target.detachEvent("on" + eventType, handler);
};
适用于页面加载后要立即执行的功能。
====================================================================
使用执行效率最高的方法
位操作符
在基于32位二进制表达上进行的位运算是最快的,而且其实JS支持这些位操作符:&, |,^和~,甚至>>,<<和>>>。
比如下面的代码:
for (var i = 0, len = rows.length; i < len; i++) {
if (i % 2) {
className = "even";
} else {
className = "odd";
}
//apply class
}
换成下面的方案后执行起来会快很多:
for (var i = 0, len = rows.length; i < len; i++) {
if (i & 1) {
className = "odd";
} else {
className = "even";
}
//apply class
}
另外还有个位掩码(bitmask)的方案也很经典,这个我曾经在SQL的层面用过:
var OPTION_A = 1;
var OPTION_B = 2;
var OPTION_C = 4;
var OPTION_D = 8;
var OPTION_E = 16;
var options = OPTION_A | OPTION_C | OPTION_D;
//is option A in the list?
if (options & OPTION_A) {
//do something
}
//is option B in the list?
if (options & OPTION_B) {
//do something
}
原生态方法
就像Math这种內建对象的方法比如Math.acos(x)其实都是在更低的层面上实现的,会快很多。
还有查找DOM元素的两个方法:querySelector()和querySelectorAll()。
Chapter 9: 构建与配置高性能的JavaScript应用程序
这一章讲的东西跟写代码没有直接关系。是在构建大型项目时会用到的构建工具和一些用于优化性能的工具。
这些工具可以做的事情包括:
- 把若干个JS文件整合成一个JS文件;
- 有条件性地插入一些代码,比如根据不同开发阶段插入不同用于调试的代码;
- 修改新版本文件名来解决缓存技术导致的文件更新问题;
- 上传新版文件至远端服务器,类似CDN;
- 等等
作者是以Ant为例子展示这些动能的,不过近年来貌似没有人再用Ant了,完全没有听人讨论过。
其他的相关工具和方法还包括:
- 给JS文件瘦身,用类似YUI Compressor和Packer这样的工具;
- 在服务器端开启压缩功能,使用gzip这样的编码格式传输文件;
- 缓存JS文件,避免多余的HTTP请求,
- 使用CDN这样的分布式工具;
- 等等。
最后作者推荐一个Yahoo自己的工具:smasher,它能完成大多数上面提到的功能。
Chapter 10: 工具
这一章介绍的工具主要用于两个方面,分析JS程序的性能,和分析页面加载过程的瓶颈。
值得注意的是,有些优化性能的方案是受限于浏览器的,它或许反而导致在其他浏览器上效率变差。
分析性能
YUI Profiler
匿名函数
为了配合性能分析工具,最好还是不要使用匿名函数,而且内联呼叫也不如显式地赋值函数再调用。
比如下面的写法:
myNode.onclick = function() {
myApp.loadData();
};
不如换做:
myApp._onClick = function() {
myApp.loadData();
};
myNode.onclick = myApp._onClick;
寻找被阻滞的脚本文件
Page Speed
依然在发展中。
Fiddler
这个我一直有用。
YSlow
从项目的Git主页来看,最后一次更新是在2014年三月,也就是说它的开发已经被停止三年了,等同于被抛弃的项目。
dynaTrace Ajax Edition
这个工具不是免费的。
顺藤摸瓜找到的一些链接:
15 Website Speed Test Tools for Analyzing Web Performance
Must See JavaScript Dev Tools That Put Other Dev Tools to Shame