优化 JavaScript 代码的方法小结


Posted in Javascript onJuly 16, 2009

优化 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); 
}

更比下面的代码慢:
function alertMsg() { 
var msg = '要显示的消息'; 
alert(msg); 
} 
function setupAlertTimeout() { 
window.setTimeout(alertMsg, 100); 
}

他们增加了作用域链(scope chain)的层级. 当浏览器解析属性时, 作用域链的每一个层级都必须被检查一次. 在下面的例子中:
var a = 'a'; 
function createFunctionWithClosure() { 
var b = 'b'; 
return function () { 
var c = 'c'; 
a; c; 
}; 
} 
var f = createFunctionWithClosure(); 
f();

当 f 被调用时, 引用 a 比引用 b 慢, 它们都比引用 c 要慢.

查看 IE+JScript Performance Recommendations Part 3: JavaScript Code inefficiencies 获得更多有关在 IE 中使用闭包的信息.

避免使用 with

在你的代码中避免使用 with. 它对性能有非常坏的影响, 因为它修改了作用域链, 让查找在其它作用域的变量变得代价高昂.

避免浏览器内存泄漏

内存泄漏对 Web 应用而言是个很普遍的问题, 它会带来严重的性能问题. 当浏览器的内存使用上升时, 你的 Web 应用, 连同用户系统的其他部分, 都会变慢. Web 应用最常见的内存泄漏原因是: 在 JavaScript 脚本引擎和浏览器 DOM 的 C++ 对象实现间的循环引用(例如, 在 JavaScript 脚本引擎和 Internet Explorer 的 COM 基础架构间, 或者 JavaScript 引擎和 Firefox 的 XPCOM 基础架构间).

下面是避免内存泄漏的一些经验法则:

使用一个事件系统来附加事件处理函数

最常见的循环引用模式 [ DOM 元素 --> 事件处理函数 --> 闭包作用域 --> DOM ] 在 这篇 MSDN 的 Blog 文章中讨论过了. 为避免这个问题, 可以使用一个经过严格测试的事件系统来附件事件处理函数, 例如 Google doctype, Dojo, or JQuery.

另外, 在 IE 中使用内联(inline)的事件处理函数会导致另外一类泄漏. 这不是通常的循环引用泄漏, 而是内存中临时匿名脚本对象的泄漏. 详情请查看 理解和解决 IE 泄漏模式(Understanding and Solving Internet Explorer Leak Patterns) 的 "DOM 插入顺序泄漏模型(DOM Insertion Order Leak Model)" 一节, 另外在 JavaScript Kit 教程 中还有一个例子.

避免使用扩展(expando)属性

扩展属性是附加到 DOM 元素上的任意 JavaScript 属性, 也是循环引用的常见原因. 你能够在使用扩展属性时不导致内存泄漏, 但是很容易不小心就引入一个泄漏. 这个泄漏的模式是 [ DOM 元素 --> 扩展属性 --> 中间对象 --> DOM 元素 ]. 最好的方法就是避免使用它们. 如果你要使用它们, 就只使用简单的值类型. 如果你要非简单的类型, 那么在不再需要扩展属性时将它设为空(null). 参见 理解和解决 IE 泄漏模式(Understanding and Solving Internet Explorer Leak Patterns) 中的 "循环引用(Circular References)" 一节.

Javascript 相关文章推荐
取键盘键位ASCII码的网页
Jul 30 Javascript
js 匿名调用实现代码
Jun 19 Javascript
javascript算法学习(直接插入排序)
Apr 12 Javascript
Js获取数组最大和最小值示例代码
Oct 29 Javascript
JavaScript字符串常用的方法
Mar 10 Javascript
jQuery中$.grep() 过滤函数 数组过滤
Nov 22 Javascript
完美解决JS文件页面加载时的阻塞问题
Dec 18 Javascript
JS身份证信息验证正则表达式
Jun 12 Javascript
jQuery.form.js的使用详解
Jun 14 jQuery
JavaScript实现的仿新浪微博原生态输入字数即时检查功能【兼容IE6】
Sep 26 Javascript
django使用channels2.x实现实时通讯
Nov 28 Javascript
Element-UI+Vue模式使用总结
Jan 02 Javascript
Javascript 事件流和事件绑定
Jul 16 #Javascript
js 对象是否存在判断
Jul 15 #Javascript
js 实现无缝滚动 兼容IE和FF
Jul 15 #Javascript
兼容IE/Firefox/Opera/Safari的检测页面装载完毕的脚本Ext.onReady的实现
Jul 14 #Javascript
JavaScript 继承详解(四)
Jul 13 #Javascript
JavaScript 继承详解(三)
Jul 13 #Javascript
JavaScript 继承详解(二)
Jul 13 #Javascript
You might like
php记录代码执行时间(实现代码)
2013/07/05 PHP
xss防御之php利用httponly防xss攻击
2014/03/21 PHP
PHP 的比较运算与逻辑运算详解
2016/05/12 PHP
用PHP写的一个冒泡排序法的函数简单实例
2016/05/26 PHP
Laravel学习教程之View模块详解
2017/09/18 PHP
PHP数据对象映射模式实例分析
2019/03/29 PHP
jquery蒙版控件实现代码
2010/12/08 Javascript
JS获得图片alt信息的方法
2015/04/01 Javascript
javaScript中push函数用法实例分析
2015/06/08 Javascript
angularJs关于指令的一些冷门属性详解
2016/10/24 Javascript
js实现上传图片预览方法
2016/10/25 Javascript
微信小程序(三):网络请求
2017/01/13 Javascript
简单谈谈关于 npm 5.0 的新坑
2017/06/08 Javascript
详解JavaScript添加给定的标签选项
2018/09/17 Javascript
react 组件传值的三种方法
2019/06/03 Javascript
Vue.set 全局操作简单示例
2019/09/19 Javascript
vue更改数组中的值实例代码详解
2020/02/07 Javascript
Angular 多模块项目构建过程
2020/02/13 Javascript
vue实现几秒后跳转新页面代码
2020/09/09 Javascript
原生JavaScript实现幻灯片效果
2021/02/19 Javascript
[02:22]《新闻直播间》2017年08月14日
2017/08/15 DOTA
python实现装饰器、描述符
2018/02/28 Python
11个Python3字典内置方法大全与示例汇总
2019/05/13 Python
解决Mac下使用python的坑
2019/08/13 Python
Python同时迭代多个序列的方法
2020/07/28 Python
Python如何将字符串转换为日期
2020/07/31 Python
python计算auc的方法
2020/09/09 Python
python实现简单文件读写函数
2021/02/25 Python
纯HTML+CSS3制作导航菜单(附源码)
2013/04/24 HTML / CSS
手工制作的意大利皮革运动鞋:KOIO
2020/01/05 全球购物
KOHLER科勒美国官网:国际著名卫浴橱柜领先品牌
2020/06/27 全球购物
工业自动化专业毕业生推荐信
2013/11/18 职场文书
共产党员公开承诺践诺书
2014/05/28 职场文书
调研汇报材料范文
2014/08/17 职场文书
群众路线剖析材料怎么写
2014/10/09 职场文书
矛盾论读书笔记
2015/06/29 职场文书