首先看下面一段代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="content-type" />
<script src="jquery-1.3.2.js" type="text/javascript">
</script>
<style type="text/css">
#inner {
margin:0 auto;
width:150px;
height:50px;
border:1px solid green;
}
</style>
<script type="text/javascript">
$(function(){
$("#inner").click(function(){
//错误,引起内存泄露
$("#test")[0].innerHTML="";
//正确做法
//$("#test").empty();
});
});
</script>
<title>测试</title>
</head>
<body>
<div style="height:500px;width:500px;border:1px solid red;padding-top:100px;" id="test">
<div id="inner">click to remove me</div>
</div>
</body>
</html>
场景:
inner div 就是常见的我们可能使用 ajax 从服务器动态构造的元素,或者注入的片断html,效果为点击 inner 后就把它以及它的兄弟清除出去,依照传统的思路,直接 innerHTML =“” 就好了 ,可能你会想到所有删除元素的事件监听器应该清楚掉,但是对于除了 ie6 (引起内存泄露)以外的浏览器,清除事件监听器不是必要的 。
jquery 机制:
但是可以确定的是在 jquery 中用 innerHTML 的方法来清空元素,是必然会导致内存泄露的,由于 jquery 对于同一元素多事件处理没有直接采用浏览器事件模型,而是自己缓存事件,遍历触发,以及便于 trigger 程序触发 :
// Init the element's event structure var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}), handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){ // Handle the second event of a trigger and when // an event is called after a page has unloaded return typeof jQuery !== "undefined" && !jQuery.event.triggered ? jQuery.event.handle.apply(arguments.callee.elem, arguments) : undefined; });
采用 data 方法,将一些数据关联到了元素上面,上述事件即是采用该机制缓存事件监听器。
那么就可以知道,直接 innerHTML=“” 而不通知 jquery 清空与将要删除元素关联的数据,那么这部分数据就再也释放不了了,即为内存泄露。
解决:
jquery 已经想到了这一方面,提供了 empty 函数,其思想就是,对节点的所有子,孙,重孙.....节点( $("*",this) ),先清空它们关联的数据,再进行节点的删除:
remove: function( selector ) { if ( !selector || jQuery.filter( selector, [ this ] ).length ) { // Prevent memory leaks jQuery( "*", this ).add([this]).each(function(){ jQuery.event.remove(this); jQuery.removeData(this); }); if (this.parentNode) this.parentNode.removeChild( this ); } }, empty: function() { // Remove element nodes and prevent memory leaks jQuery(this).children().remove(); // Remove any remaining nodes while ( this.firstChild ) this.removeChild( this.firstChild ); }