优化 JavaScript 代码 
作者: Gregory Baker, GMail 软件工程师 和 Erik Arvidsson, Google Chrome 软件工程师 
需要的经验: JavaScript 相关工作知识 
客户端脚本能让你的应用更加地动态和活跃, 但是浏览器对代码的解析可能造成效率问题, 而这种性能差异在客户端之间也不尽相同. 这里我们讨论和给出一些优化你的 JavaScript 代码的提示和最佳实践. 
使用字符串 
字符串连接操作会对 Internet Explorer 6 和 7 的垃圾收集带来很大的影响. 尽管这个问题在 Internet Explorer 8 里面得到解决 -- 字符串连接在 IE8 和其它非 IE 浏览器(如 Chrome)中稍微更有效率一点 -- 如果你的用户中有很大一部分在使用 Internet Explorer 6 或 7, 你就需要非常注意你构建字符串的方式了. 
有如下示例代码: 
复制代码 代码如下:
 
var veryLongMessage = 
'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():
复制代码 代码如下:
 
var veryLongMessage = 
['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(); 
相似的, 用连接的方式在条件语句和循环中构建字符串是很低效的. 错误的方式:
复制代码 代码如下:
 
var fibonacciStr = '前 20 个斐波那契数 '; 
for (var i = 0; i < 20; i++) { 
fibonacciStr += i + ' = ' + fibonacci(i) + ' 
'; 
} 
正确的方法:
复制代码 代码如下:
 
var strBuilder = ['前 20 个斐波那契数:']; 
for (var i = 0; i < 20; i++) { 
strBuilder.push(i, ' = ', fibonacci(i)); 
} 
var fibonacciStr = strBuilder.join(''); 
构建通过辅助函数生成的字符串
通过传递字符串构建器(可以是数组或者辅助类)到函数中构建长字符串, 以避免出现存放临时结果的字符串.
例如, 假定 buildMenuItemHtml_ 需要用文字串和变量构建一个字符串, 并且会在内部使用一个字符串构建器, 与其使用:
复制代码 代码如下:
 
var strBuilder = []; 
for (var i = 0; i < menuItems.length; i++) { 
strBuilder.push(this.buildMenuItemHtml_(menuItems[i])); 
} 
var menuHtml = strBuilder.join(); 
不如用:
复制代码 代码如下:
 
var strBuilder = []; 
for (var i = 0; i < menuItems.length; i++) { 
this.buildMenuItem_(menuItems[i], strBuilder); 
} 
var menuHtml = strBuilder.join(); 
定义类的方法
下面的代码效率不高, 因为每次构造 baz.Bar 的实例时, 都会为 foo 创建一个新函数和闭包(closure):
复制代码 代码如下:
 
baz.Bar = function() { 
// 构造函数代码 
this.foo = function() { 
// 方法代码 
}; 
} 
推荐的方式为:
复制代码 代码如下:
 
baz.Bar = function() { 
// 构造函数代码 
}; 
baz.Bar.prototype.foo = function() { 
// 方法代码 
}; 
用这种方式, 无论构造了多少个 baz.Bar 实例, 只会创建一个函数给 foo, 同时不会创建任何闭包.
初始化实例变量
将带有值类型(非引用的)的初始化值(例如类型为数字, 布尔值, null, undefined 或字符串的值)的变量声明/初始化代码直接放在 prototype 原型中. 这可以避免每次调用构造函数时不必要地运行初始化代码. (这个方法无法应用到初始化值由构造器参数决定或构造时状态不确定的实例变量上.)
例如, 比起写:
复制代码 代码如下:
 
foo.Bar = function() { 
this.prop1_ = 4; 
this.prop2_ = true; 
this.prop3_ = []; 
this.prop4_ = 'blah'; 
}; 
不如写:
复制代码 代码如下:
 
foo.Bar = function() { 
this.prop3_ = []; 
}; 
foo.Bar.prototype.prop1_ = 4; 
foo.Bar.prototype.prop2_ = true; 
foo.Bar.prototype.prop4_ = 'blah'; 
谨慎地使用闭包(closure)
闭包是 JavaScript 中一个强大而有用的特性; 但是, 它们也有不好的地方, 包括:
它们是最常见的内存泄漏源头.
创建一个闭包比创建一个没有闭包的内联函数明显要慢, 比起重用一个静态函数则更慢. 例如:
复制代码 代码如下:
 
function setupAlertTimeout() { 
var msg = '要显示的消息'; 
window.setTimeout(function() { alert(msg); }, 100); 
} 
比下面的代码慢:
复制代码 代码如下:
 
function setupAlertTimeout() { 
window.setTimeout(function() { 
var msg = '要显示的消息'; 
alert(msg); 
}, 100); 
} 
更比下面的代码慢:
复制代码 代码如下:
