1.注意作用域
(1)避免全局查找:
将在一个函数中会用到多次的全局对象存储为局部变量总是没错的。
function updateUI(){
var imgs = document.getElementsByTagName('img');
for (var i = 0; i < imgs.length; i++) {
imgs[i].title = document.title + " image " + i;
}
//........
}
// 函数有两次对全局对象document对象的引用。如果页面上有多个图片,
// 那么for循环中document引用就会被执行多次,每次都要进行作用域链的查找
function updateUI(){
var doc = document;
var imgs = doc.getElementsByTagName('img');
for (var i = 0; i < imgs.length; i++) {
imgs[i].title = doc.title + " image " + i;
}
//........
}
// 为了优化代码,可以通过创建一个局部变量指向document对象,
// 就可以通过限制一次全局查找来优化这个函数的性能
(2)避免with语句
和函数类似,with语句会创建自己的作用域,因此会增加其中执行代码的作用域的长度。由于额外的作用域链查找,在with语句中执行的代码肯定比外面执行的代码慢。
function updateUI(){
with(document.body){
alert(tagName);
innerHTML = "hello";
}
}
应该改为:
function updateUI(){
var body = document.body;
alert(body.tagName);
body.innerHTML = "hello";
}
原因:标识符解析是沿着作用域链一级一级地搜索标识符的过程,搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直到找到标识符为止。
总结:访问全局变量总要比访问局部变量慢,因为需要遍历作用域链。只要能减少花费在作用域链上的时间,就能增加脚本的整体性能。
2.选择正确方法
(1)避免不必要的属性查找
例如:
var query = window.location.href.substring(window.location.href.indexOf("?"));
上述语句,进行了6次属性查找
应该改为,进行了4次属性查找,比原始版本节省33%的属性查找时间。
var url = window.location.href;
var query = url.substring(url.indexOf("?"));
总结:只要能减少算法的复杂度,就要尽可能减少。尽可能多地使用局部变量将属性查找替换为值查找。进一步讲,如果可以用数字化的数组位置进行访问,也可以用命名属性,那么使用数字位置。
(2)优化循环
①减值迭代
for (var i = 0; i < array.length; i++) {
fn(array[i]);
}
每次循环i都会与array.length进行比较,涉及属性查找,时间复杂度O(n)
for (var i = array.length-1; i >=0; i--) {
fn(array[i]);
}
改为与常数0比较,时间复杂度变为O(1)
②简化终止条件
由于每次循环过程都会计算终止条件,所以必须保证它尽可能快,也就是避免属性查找以及其他O(n)操作
上述例子改为减值迭代,但如果函数内的数据处理与顺序有关,无法使用减值迭代,则应优化为
for (var i = 0,len=array.length; i < len; i++) {
fn(array[i]);
}
那么前面提到的避免全局查找的例子也可以再进一步优化终止条件
function updateUI(){
var doc = document;
var imgs = doc.getElementsByTagName('img');
for (var i = 0,len=imgs.length; i < len; i++) {
imgs[i].title = doc.title + " image " + i;
}
//........
}
// 为了优化代码,可以通过创建一个局部变量指向document对象,
// 就可以通过限制一次全局查找来优化这个函数的性能
③简化循环体
循环体执行最多,确保最大限度被优化,确保没有某些可以很容易被移出循环体的密集计算。
④使用后测试循环
for循环,while循环属于前测试循环。而do-while属于后测试循环,可以避免最初终止条件的计算,因此运行更快、
(3)展开循环
当循环的次数确定,消除循环并多次使用函数调用往往更快。
for (var i = 0; i < 3; i++) {
fn(array[i]);
}
fn(array[0]);
fn(array[1]);
fn(array[2]);
第二种方法比第一种更快
(4)避免双重解释
尽可能避免出现需要按照javascript解析的字符串
例如
eval("alert('hello!')");
var fn = new Function("alert('hello!')");
setTimeout("alert('hello!')",500);
(5)原生方法较快
(6)Switch语句较快
(7)位运算符较快
3.最小化语句数
(1)多个变量声明合并成一条语句
var a = 1;
var b = 5;
var c = "red";
应该优化为
var a = 1,
b = 5,
c = "red";
(2)插入迭代值
var age = person[i];
i++;
应该优化为
var age = person[i++];
(3)使用数组和对象字面量
var arr = new Array();
arr[0] = 1;
arr[1] = 50;
arr[2] = 7;
应该优化为
var arr = [1,50,4];
var obj = new Object();
obj.name = "Jack";
obj.age = 27;
obj.sayName = function(){
return this.name;
}
应该优化为
var obj = {
name:"Jack",
age:27,
sayName:function(){
return(this.name);
}
};