- 减少不必要的对象创建:
- 创建对象本身对性能影响并不大,但由于
JAVASCRIPT
的垃圾回收调度算法,导致随着对象个数的增加,性能会开始严重下降(复杂度O(n^2)
)。- 如常见的字符串拼接问题,单纯的多次创建字符串对象其实根本不是降低性能的主要原因,而是是在对象创建期间的无谓的垃圾回收的开销。而
Array.join
的方式,不会创建中间字符串对象,因此就减少了垃圾回收的开销。
- 如常见的字符串拼接问题,单纯的多次创建字符串对象其实根本不是降低性能的主要原因,而是是在对象创建期间的无谓的垃圾回收的开销。而
- 复杂的
JAVASCRIPT
对象,其创建时时间和空间的开销都很大,应该尽量考虑采用缓存。 - 尽量作用
JSON
格式来创建对象,而不是var obj=new Object()
方法。前者是直接复制,而后者需要调用构造器。
- 创建对象本身对性能影响并不大,但由于
- 对象查找
- 避免对象的嵌套查询,因为
JAVASCRIPT
的解释性,a.b.c.d.e
嵌套对象,需要进行4
次查询,嵌套的对象成员会明显影响性能。 - 如果出现嵌套对象,可以利用局部变量,把它放入一个临时的地方进行查询。
- 避免对象的嵌套查询,因为
- 对象属性
- 访问对象属性消耗性能过程(
JAVASCRIPT
对象存储)。- 先从本地变量表找到
对象
。 - 然后遍历
属性
。 - 如果在
当前对象
的属性列表
里没找到。 - 继续从
prototype
向上查找。 - 且不能直接索引,只能遍历。
- 先从本地变量表找到
- 访问对象属性消耗性能过程(
function f(obj) {
return obj.a + 1;
}
- 当需要使用数组时,可使用
JSON
格式的语法- 即直接使用如下语法定义数组:
[parrm,param,param...]
,而不是采用new Array(parrm,param,param...)
这种语法。使用JSON
格式的语法是引擎直接解释。而后者则需要调用Array
的构造器。
- 即直接使用如下语法定义数组:
- 如果需要遍历数组,应该先缓存数组长度,将数组长度放入局部变量中,避免多次查询数组长度。
- 根据字符串、数组的长度进行循环,而通常这个长度是不变的,比如每次查询
a.length
,就要额外进行一个操作,而预先把var len=a.length
,则每次循环就少了一次查询。
- 根据字符串、数组的长度进行循环,而通常这个长度是不变的,比如每次查询
- 使用运算符时,尽量使用
+=
,-=
、*=
、\=
等运算符号,而不是直接进行赋值运算。 位运算
。- 当进行数学运算时
位运算
较快,位运算
操作要比任何布尔运算
或算数运算
快,如取模
,逻辑与
和逻辑或
也可以考虑用位运算
来替换。
- 当进行数学运算时
- 对字符串进行循环操作。
- 替换、查找等操作,使用正则表达式。
- 因为
JAVASCRIPT
的循环速度较慢,而正则表达式的操作是用C
写成的API
,性能比较好。
- 因为
- 替换、查找等操作,使用正则表达式。
- 字符串的拼接。
- 字符串的拼接在我们开发中会经常遇到,所以我把其放在首位,我们往往习惯的直接用
+=
的方式来拼接字符串,其实这种拼接的方式效率非常的低,我们可以用一种巧妙的方法来实现字符串的拼接,那就是利用数组的join
方法,具体请看我整理的:Web前端开发规范文档中的javaScript书写规范
倒数第三条目。 - 不过也有另一种说法,通常认为需要用
Array.join
的方式,但是由于SpiderMonkey
等引擎对字符串的“+
”运算做了优化,结果使用Array.join
的效率反而不如直接用“+
”,但是如果考虑IE6
,则其他浏览器上的这种效率的差别根本不值一提。具体怎么取舍,诸君自定。
- 字符串的拼接在我们开发中会经常遇到,所以我把其放在首位,我们往往习惯的直接用
- 循环是一种常用的流程控制。
JAVASCRIPT
提供了三种循环。for(;;)
。- 推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的
++
或–-
运算符。 - 代码的可读性对于for循环的优化。
- 用
-=1
。 - 从大到小的方式循环(这样缺点是降低代码的可读性)。
- 推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的
/**效率低**/
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++){
...
}
/**效率高,适用于获取DOM集合,如果纯数组则两种情况区别不到**/
var divs = document.getElementsByTagName("div");
for(var i = 0, len = divs.length; i < len; i++){
...
}
/**在`IE6.0`下,`for(;;)`循环在执行中,第一种情况会每次都计算一下长度,而第二种情况却是在开始的时候计算长度,并把其保存到一个变量中,所以其执行效率要高点,所以在我们使用`for(;;)`循环的时候,特别是需要计算长度的情况,我们应该开始将其保存到一个变量中。**/
- `while()`。
- `for(;;)`、`while()`循环的性能基本持平。
- `for(in)`。
- 在这三种循环中`for(in)`内部实现是构造一个所有元素的列表,包括`array`继承的属性,然后再开始循环,并且需要查询hasOwnProperty。所以`for(in)`相对`for(;;)`循环性能要慢。
- 选择正确的方法 - 避免不必要的属性查找。 - 访问
变量
或数组
是O(1)
操作。 - 访问对象
上的属性
是一个O(n)
操作。
对象上的任何属性查找都要比访问变量或数组花费更长时间,因为必须在原型链中对拥有该名称的属性进行一次搜索,即属性查找越多,执行时间越长。所以针对需要多次用到对象属性,应将其存储在局部变量。
- 优化循环。
- 减值迭代。
- 大多数循环使用一个从0开始,增加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更加有效。
- 简化终止条件。
- 由于每次循环过程都会计算终止条件,故必须保证它尽可能快,即避免属性查找或其它O(n)的操作。
- 简化循环体。
- 循环体是执行最多的,故要确保其被最大限度地优化。确保没有某些可以被很容易移出循环的密集计算。
- 使用后测试循环。
- 最常用的for和while循环都是前测试循环,而如do-while循环可以避免最初终止条件的计算,因些计算更快。
>
for(var i = 0; i < values.length; i++) {
process(values[i]);
}
>
优化1:简化终止条件
>
for(var i = 0, len = values.length; i < len; i++) {
process(values[i]);
}
>
优化2:使用后测试循环(注意:使用后测试循环需要确保要处理的值至少有一个)
>
var i values.length - 1;
if(i > -1) {
do {
process(values[i]);
}while(--i >= 0);
}
- 展开循环。
- 当循环的次数确定时,消除循环并使用多次函数调用往往更快。
- 当循环的次数不确定时,可以使用Duff装置来优化。
- Duff装置的基本概念是通过计算迭代的次数是否为8的倍数将一个循环展开为一系列语句。
>
// Jeff Greenberg for JS implementation of Duff's Device
// 假设:values.length > 0
function process(v) {
alert(v);
}
var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
var iterations = Math.ceil(values.length / 8);
var startAt = values.length % 8;
var i = 0;
do {
switch(startAt) {
case 0 : process(values[i++]);
case 7 : process(values[i++]);
case 6 : process(values[i++]);
case 5 : process(values[i++]);
case 4 : process(values[i++]);
case 3 : process(values[i++]);
case 2 : process(values[i++]);
case 1 : process(values[i++]);
}
startAt = 0;
}while(--iterations > 0);
>
如上展开循环可以提升大数据集的处理速度。接下来给出更快的Duff装置技术,将do-while循环分成2个单独的循环。(注:这种方法几乎比原始的Duff装置实现快上40%。)
>
// Speed Up Your Site(New Riders, 2003)
function process(v) {
alert(v);
}
var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
var iterations = Math.floor(values.length / 8);
var leftover = values.length % 8;
var i = 0;
if(leftover > 0) {
do {
process(values[i++]);
}while(--leftover > 0);
}
do {
process(values[i++]);
process(values[i++]);
process(values[i++]);
process(values[i++]);
process(values[i++]);
process(values[i++]);
process(values[i++]);
process(values[i++]);
}while(--iterations > 0);
>
针对大数据集使用展开循环可以节省很多时间,但对于小数据集,额外的开销则可能得不偿失。
- 避免在循环中使用
try-catch
。try-catch-finally
语句在catch语句被执行的过程中会动态构造变量插入到当前域中,对性能有一定影响。- 如果需要异常处理机制,可以将其放在循环外层使用。
- 循环中使用try-catch
for ( var i = 0; i < 200; i++) {
try {} catch (e) {}
}
- 循环外使用try-catch
try {
for ( var i = 0; i < 200; i++) {}
} catch (e) {}
- 避免遍历大量元素: - 避免对全局
DOM
元素进行遍历,如果parent
已知可以指定parent
在特定范围查询。
var elements = document.getElementsByTagName( '*' );
for (i = 0; i < elements.length; i++) {
if (elements[i].hasAttribute( 'selected' )) {}
}
如果已知元素存在于一个较小的范围内,
var elements = document.getElementById( 'canvas' ).getElementsByTagName ( '*' );
for (i = 0; i < elements.length; i++) {
if (elements[i].hasAttribute( 'selected' )) {}
}
###eval优化
- 避免
eval
:eval
会在时间方面带来一些效率,但也有很多缺点。eval
会导致代码看起来更脏。eval
会需要消耗大量时间。eval
会逃过大多数压缩工具的压缩。
-
尽可能地少用
with
语句,因为它会增加with
语句以外的数据的访问代价。 -
避免使用
with
with
语句将一个新的可变对象推入作用域链的头部,函数的所有局部变量现在处于第二个作用域链对象中,从而使局部变量的访问代价提高。var person = { name: “Nicholas", age: 30 } function displayInfo() { var count = 5; with (person) { alert(name + ' is ' + age); alert( 'count is ' + count); } }
- 减少 COOKIE 的大小。
- 使用无 COOKIE 的域。
- 比如图片 CSS 等静态文件放在静态资源服务器上并配置单独域名,客户端请求静态文件的时候,减少 COOKIE 反复传输时对主域名的影响。
- js性能优化和内存泄露问题及检测分析工具
- 性能优化ajax工具`diviefirebug`
- [web性能分析工具YSlow]
- `performance`性能评估打分,右击箭头可看到改进建议。
- `stats`缓存状态分析,传输内容分析。
- `components`所有加载内容分析,可以查看传输速度,找出页面访问慢的瓶颈。
- `tools`可以查看js和css,并打印页面评估报告。
- 内存泄露检测工具`sIEve`
- `sIEve`是基于`IE`的内存泄露检测工具,需要下载运行,可以查看dom孤立节点和内存泄露及内存使用情况。
1. 列出当前页面内所有dom节点的基本信息(html id style 等)
1. 页面内所有dom节点的高级信息 (内存占用,数量,节点的引用)
1. 可以查找出页面中的孤立节点
1. 可以查找出页面中的循环引用
1. 可以查找出页面中产生内存泄露的节点
- 内存泄露提示工具`leak monitor`
- `leak monitor`在安装后,当离开一个页面时,比如关闭窗口,如果页面有内存泄露,会弹出一个文本框进行即时提示。
- 代码压缩工具
- YUI压缩工具
- Dean Edwards Packer
- JSMin
- `Blink/Webkit`浏览器
- 在`Blink/Webkit`浏览器中(`Chrome`, `Safari`, `Opera`),我们可以借助其中的`Developer Tools`的`Profiles`工具来对我们的程序进行内存检查。
Developer Tools - Profiles
Node.js
中的内存检查- 在
Node.js
中,我们可以使用node-heapdump
和node-memwatch
模块进行内存检查。
- 在
var heapdump = require('heapdump');
var fs = require('fs');
var path = require('path');
fs.writeFileSync(path.join(__dirname, 'app.pid'), process.pid);
在业务代码中引入`node-heapdump`之后,我们需要在某个运行时期,向`Node.js`进程发送`SIGUSR2`信号,让`node-heapdump`抓拍一份堆内存的快照。
$ kill -USR2 (cat app.pid)
这样在文件目录下会有一个以`heapdump-<sec>.<usec>.heapsnapshot`格式命名的快照文件,我们可以使用浏览器的`Developer Tools`中的`Profiles`工具将其打开,并进行检查。
- 分析浏览器提供的Waterfall图片来思考优化入口。
- 新的测试手段(Navigation, Resource, 和User timing。