尽量减少DOM访问
DOM的访问是需要搜索的,这个的开销很大。如果有些元素需要经常使用,那就用个变量把它缓存起来。
压缩代码
用javascript压缩工具把代码压缩一下,这样可以缩短下载javascript文件的时间。限制DOM操作引发的回流
style属性对外观进行修改的时候会触发回流操作,可以使用更改className的方式替换修改style属性。添加多个dom元素时,先将元素append到DocumentFragment中,最后统一将DocumentFragment添加到页面。该做法可以减少页面渲染dom元素的次数。
HTMLCollection访问length时把length属性保存到变量中。
数组访问length属性和HtmlCollection访问length有很在的区别。在数组中,length是一个普通的属性,访问时不会进行额外的操作,虽然我们常常将HTMLCollection当作数组来使用,但实际上,它是一个根据DOM结构自动变化的实体对象。每次访问一个HTMLCollection对象的属性时,他都会对DOM内所有的节点进行一次完整的匹配。也就是说,每次访问HtmlCollection对象的length时,都会更新一次集合对象,性能上消耗很大。所以一般情况之下,这种HtmlCollection的循环操作,都是建议缓存length的。
通过模板元素clone,替代createElement
通常我们可能会使用字符串直接写HTML来创建节点,其实这样做效率比较低。
通过一个模板dom对象cloneNode,效率比直接创建element高。性能提高不明显,约为10%左右。在低于100个元素create和append操作时,没有优势。
如果要创建很多的元素,则最好先准备个样板。
使用firstChild和nextSibling代替childNodes遍历dom元素
约能获得30%-50%的性能提高。逆向遍历时使用lastChild和previousSibling。
比如下面这样写效率不高:
可以改成这样:
比如下面这样写效率不高:
var nodes = element.childNodes;
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i];
……
}
可以改成这样:
var node = element.firstChild;
while (node) {
……
node = node.nextSibling;
}
避免内存泄露
如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。
简单的循环引用:
但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。
如:
init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。
下面2种方法可以解决循环引用:
1) 置空dom对象
优化前:
优化后:
将el置空,context中不包含对dom对象的引用,从而打断循环应用。
如果我们需要将dom对象返回,可以用如下方法:
优化前:
优化后:
2) 构造新的context
优化前:
优化后:
把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。
简单的循环引用:
var el = document.getElementById('MyElement');
var func = function () {…}
el.func = func;
func.element = el;
但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。
如:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
}
init();
init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。
下面2种方法可以解决循环引用:
1) 置空dom对象
优化前:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
}
init();
优化后:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
el = null;
}
init();
将el置空,context中不包含对dom对象的引用,从而打断循环应用。
如果我们需要将dom对象返回,可以用如下方法:
优化前:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
return el;
}
init();
优化后:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
try{
return el;
} finally {
el = null;
}
}
init();
2) 构造新的context
优化前:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
}
init();
优化后:
function elClickHandler() {……}
function init() {
var el = document.getElementById('MyElement');
el.onclick = elClickHandler;
}
init();
把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。
减少不必要的全局变量
全局变量会加大内存的开销,而且在函数中访问局部变量的时间要短于全局变量的时间。
减少对象的查找
因为JavaScript的解释性,所以a.b.c.d.e,需要进行至少4次查询操作,先检查a再检查a中的b,再检查b中的c,如此往下。所以如果这样的表达式重复出现,只要可能,应该尽量少出现这样的表达式,可以利用局部变量,把它放入一个临时的地方进行查询。不过对于命名空间的问题,大概是牺牲少量的性能获得更优的代码组织形式吧。
缓存高级对象
自定义高级对象和Date、RegExp对象在构造时都会消耗大量时间。如果可以复用,应采用缓存的方式。