见www.yeeyan.com/articles/view/92135/47626/dz
原文见:http://code.google.com/intl/zh-CN/speed/articles/optimizing-javascript.html
不合适的地方,请大家指出来!希望对你有用!
优化JavaScript代码(Optimizing JavaScript code)
客户端脚本让你的应用程序更动态,但是浏览器解释脚本会带来低效率,不同客户端的性能也是不同的.下面我们讨论一些技巧和最好的实践来优化你的JavaScript代码
使用字符串(working with string)
String连接会给IE6和IE7垃圾回收造成影响.尽管这些问题已经得到在IE8解决--字符串连接的效率在IE8和其它非IE浏览器(如chrome)上有稍微提高.如果你的一大部分用户群体在使用IE6,7,你应该备加注意String的构造方式.
看一下如下例子:
'This is a long string that due to our strict line length limit of' +
maxCharsPerLine +
' characters per line must be wrapped. ' +
percentWhoDislike +
'% of engineers dislike this rule. The line length limit is for ' +
' style purposes, but we don't want it to have a performance impact.' +
' So the question is how should we do the wrapping?';
尝试用join来代替连接:
2 ['This is a long string that due to our strict line length limit of',
3 maxCharsPerLine,
4 ' characters per line must be wrapped. ',
5 percentWhoDislike,
6 '% of engineers dislike this rule. The line length limit is for ',
7 ' style purposes, but we don't want it to have a performance impact.',
8 ' So the question is how should we do the wrapping?'
9 ].join();
10
同样的,在循环和/或条件语句中通过连接来构造字符串也是非常低效的.下面是一个错误的例子:
for (var i = 0; i < 20; i++) {
fibonacciStr += i + ' = ' + fibonacci(i) + '';
}
正确的方式如下:
for (var i = 0; i < 20; i++) {
strBuilder.push(i, ' = ', fibonacci(i));
}
var fibonacciStr = strBuilder.join('');
通过助手函数生成字符串(Building strings with portions coming from helper functions)
通过把字符串生成器传递到函数中来构造一个长字符串,要避免临时的String结果.
例如,假设函数buildMenuItemHtml_ 需要用文字串(literal)和变量来构造String,并在内部使用了String构造器.而不是如下方式使用:
for (var i = 0, length = menuItems.length; i < length; i++) {
strBuilder.push(this.buildMenuItemHtml_(menuItems[i]));
}
var menuHtml = strBuilder.join();
推荐如下方式:
for (var i = 0, length = menuItems.length; i < length; i++) {
this.buildMenuItem_(menuItems[i], strBuilder);
}
var menuHtml = strBuilder.join();
定义类方法(Defining class methods)
下面的代码是低效率的,每生成一次baz.Bar对象,就会为其生成一个方法和闭包.
// 构造函数, constructor body
this.foo = function() {
// 方法,method body
};
}
推荐方式是:
// 构造函数,constructor body
};
baz.Bar.prototype.foo = function() {
//方法, method body
};
这种方式,不管有多少个baz.Bar对象生成,仅仅只建立一个方法foo且没有产生闭包.
初始化实例变量(Initializing instance variables)
在原型(prototype)上用值类型[value type](而不是引用类型[reference type])声明或初始化实例变量.这避免了在每次调用构造函数时运行不必要的初始化代码.(有些情况下是不能这样做的:实例变量的初始值依赖于构造参数或是在构造时的其它状态的变量(some other state at time of construction))
例子:
this.prop1_ = 4;
this.prop2_ = true;
this.prop3_ = [];
this.prop4_ = 'blah';
};
推荐的写法:
this.prop3_ = [];
};
foo.Bar.prototype.prop1_ = 4;
foo.Bar.prototype.prop2_ = true;
foo.Bar.prototype.prop4_ = 'blah';
避开闭包的陷阱(Avoiding pitfalls with closures)
闭包是JavaScript的一个强大且有用的特色;但是它有几个缺点:
1,它们是常见的内存泄漏源
2,生成闭包明显的比生成不是闭包的内部函数慢,比调用静态函数更慢.例如
var msg = 'Message to alert';
window.setTimeout(function() { alert(msg); }, 100);
}
上面比下面的写法要慢:(上面的代码产生了闭包)
window.setTimeout(function() {
var msg = 'Message to alert';
alert(msg);
}, 100);
}
上面比下面的写法要慢:
var msg = 'Message to alert';
alert(msg);
}
function setupAlertTimeout() {
window.setTimeout(alertMsg, 100);
}
3,它们增加了变量的作用域(scope chain)的层次.当浏览器解析属性的时候,将会检查所有层次的作用域.见下面的例子:
function createFunctionWithClosure() {
var b = 'b';
return function () {
var c = 'c';
a;
b;
c;
};
}
var f = createFunctionWithClosure();
f();
当执行f方法的时候,引用a比引用b慢,引用b比引用c慢.
IE中闭包更多信息可以查看 IE+JScript Performance Recommendations Part 3: JavaScript Code inefficiencies
避免用with(Avoiding with)
在你代码中避免用with.它对性能有负面影响,因为它修改了作用域,在其它作用域查找变量的开销很大.
避免浏览器的内存泄露(Avoiding browser memory leaks)
内存泄露是web应用程序普遍存在的问题,它会产生巨大的性能危害(hit).随着浏览器内存使用增加,你的web应用程序,用户系统其它部分操作,将变的慢起来.大部分web应用程序的内存泄露是因为在 JavaScript脚本和Dom之间生成了循环引用(例如:javascript脚本和IE com结构之间,javascript脚本和Firefox xpcom结构之间)
下面是避免内存泄漏一些经验法则:
使用事件系统关联事件处理函数(Use an event system for attaching event handlers)
大部分的循环引用模式[DOM 元素-->事件处理函数(event handler)-->闭包(closure scope)-->DOM元素].为了避免这个问题,可以用经过充分测试事件系统(event system)来关联事件处理函数(event handlers),例如:Google doctype,Dojo,JQuery
另外,在IE中使用用内联事件函数(inline event handlers)会导致另一种的类型的泄露.这不是通常的循环引用类型的泄露,而是由内部临时匿名脚本对象产生的泄露.详细信息,请看文章"DOM insertin Order Leak Model"和例子JavaScript Kit tutorial.
避免扩展(expando)属性
扩展(expando)属性是把任意JavaScript的属性附加的到DOM元素上,这是循环引用产生的根源.你可以不产生内存泄露来扩展(expando)属性,但是这是很容易产生内存泄露的.泄露模式是[DOM元素-->扩展(via expando)-->中间对象(intermediary object)-->DOM元素].最好是避免使用用它们.如果你用到它们,仅可使用原始类型(primitive types)作为值.如果不是原始类型,当扩展(expando)属性不在使用的时候,要把它置为空.可以参考文章"Circular References"