导致性能降低或内存泄漏的原因是多方面的:
□:每个函数都是对象。都会占用内存。
□:内存中的对象越多,性能就越差。
目前了解的提升性能的方法有:
一、事件委托。
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个时间处理程序,就可以管理某一类型的所有事件。
例如:click事件会一直冒泡到document层,也就是说,我们可以为这个页面指定一个onclick事件处理程序,而不必给每个单击的元素分别添加事件处理程序。以下为例:
<ul id="myLinks">
<li id="to">三</li>
<li id="do">个</li>
<li id="click">li</li>
</ul>
其中包含3个被单击后会执行操作的列表项。传统做法是:
var item1 = document.getElementById("to");
var item2 = document.getElementById("do");
var item3 = document.getElementById("this");
EventUtil.addHandler(item1,"click",function(event){
location.href = "http://www.abc.com";
});
EventUtil.addHandler(item2,"click",function(event){
location.title = "change了一个title";
});
EventUtil.addHandler(item3,"click",function(event){
alert("你好")
});
如果在一个复杂Web应用中,对所有可单击的元素都采用这种方式,那么结果就会有数不清的代码用于添加事件处理程序。
此时,使用“事件委托”可以解决这个问题。
事件委托,只需要在DOM树种尽量最高的层次上添加一个事件处理程序,如下:
// 事件委托
var list = document.getElementById("myLinks");
EventUtil.addHandler(list,"click",function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);//取得事件的目标哦
switch(target.id){
case "to" :
document.title = "change了一个title";
break;
case "do" :
location.href = "http://www.abc.com";
break;
case "this" :
alert("你好");
break;
};
})
我们使用事件委托只给ul添加了一个onclick事件处理程序。由于所有的列表子元素,它们的事件会冒泡不是吗?
所以单击列表项的事件最终都会被这个函数处理。事件目标是被单击的列表项,所以通过检测id属性来决定是否采取适当的操作。注意:
□:与前面代码相比,这段代码的消耗更低,因为只取得了一个DOM,只添加了一个事件程序;
□:这种技术占用的内存更少。
□:如果可行的话,为document对象添加一个事件处理程序,用以处理页面上发生的各种特定类型的事件。优点:
△:document对象很快就可以访问,而且可以在页面生命周期的任何时点上为它添加事件处理程序(无需等待DOMContentLoaded或load事件)。只要可单击的元素呈现在页面上,就可以立即具备适当的功能;
△:在页面中设置时间处理程序所需的事件更少。只添加一个事件处理程序所需的DOM引用更少,说花的时间也更少;
△:整个页面占用的内存空间更少,能整体提升性能。
最合适采用事件委托技术的事件:click、mousedown、mouseup、keydown、keyup、keypress。
二、移除事件处理程序。
每当将事件处理程序指定给元素时,浏览器与JavaScript代码之间就会建立一个链接。
链接越多,页面执行起来就越慢。所以,在不需要的时候移除事件处理程序,也能解决一部分性能问题。
内存中留的那些过时的“空事件处理程序(dangling event handler)”,也是造成Web程序内存性能问题的主因。
如果某个元素(带有事件处理程序)被innerHTML删除,那么已经添加到元素中的事件就无法被当做垃圾回收。下面:
<div id="myDiv">
<input type="button" value="点击我吧" id="myBtn">
</div>
<script>
var btn = document.getElementById("myBtn");
btn.onclick = function(){
//something
document.getElementById("myDiv").innerHTML = "修改中。。。";
}
</script>
一个按钮,被包裹在div中,单击此按钮就讲按钮移除并替换成一条消息。
但是问题是:即使按钮移除了,它本身还带着一个事件处理程序。
所以那么最好的方式是手工断开引用:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
//something
btn.onclick = null;//表达了断开
document.getElementById("myDiv").innerHTML = "修改中。。。";
}
此时,我们先移除了按钮的事件处理程序。这样确保内存可以被再次利用,而从DOM中移除按钮也做到了干净利索。
导致“空事件处理程序”的另一种情况,就是卸载页面的时候。
IE8及早版本问题肯定是最多,但其他浏览器问题也不少。如果在页面被卸载前没有清理干净事件处理程序,那它们就会滞留在内存中。
每次加载完页面再卸载页面时(可能是两个页面来回切换,也可能是点击了刷新按钮),内存中滞留的对象数目就会增加,因为事件处理程序占用的内存并没有被释放。
一般来说,最好的方法就是在页面卸载之前,先通过onunload事件处理程序移除所有事件处理程序。
只要是通过onload事件处理程序添加的东西,最后都要通过onunload事件处理程序将它们移除。
