避免DOM对象内存泄漏的使用准则
终于,整理了一下在WEB前端脚本开发时因DOM对象造成的内存泄漏原因及解决方案。
避免DOM对象内存泄漏的使用准则
- 取自DOM元素的DOM对象,添加属性时,属性值不能是与该DOM对象同作用域下声明的函数,如:a.b=function(){};"
可选方案:
1. var b = function(){};(function(){var a = DOMNode;a.b = b;}());
2. var ret = {b:function(){},init:function(){var a = DOMNode;a.b = ret.b;};
- 如果该DOM对象被其他对象(如:b)引用,该DOM对象的属性值不能包含有对象b,如:b.push(a);a.b=b;
可选方案:
1.var data={};b.push(a);a.setAttribute('GUID') = GUID;data[a.getAttribute('GUID')][b] = b;
- DOM对象属性值不能显式的引用自己,即,不能在该DOM对象生成的作用域进行显式赋值,如:a.b=a; or a.b = [a]; or a.b = {'x':a};
可选方案:
1.var data={};b.push(a);a.setAttribute('GUID') = GUID;data[a.getAttribute('GUID')][b] = b;
结论:
- 只为DOM对象设置事件类型的属性值(注意避免准则1中的情形)
- 其他属性通过一个DOM对象的GUID进行关联,存储到一个独立的对象中去
- 由于做第二步操作会产生人为的内存浪费,所以,需要引入一个适当的垃圾清理机制对其进行清理
DOM对象泄漏测试用例:
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<div id="x">X容器 ^_^</div>
<button id="y">重置容器X中的内容</button>
<script>
var md = function(module){
if(!module){return;}
var dbtn;
var obtn = [];
var ret = {
init:function(){
/*
测试1,与DOM对象在同一作用域中申明function对象时
如果该DOM对象为私有变量,则产生于作用域链中的隐性引用也将计为有效引用,会出现泄漏
如果该DOM对象为共享对象,则因为每次会对该对象重新赋值,旧的引用自动去除,不会出现泄漏
*/
/*
var dbtn = module.getElementsByTagName('button')[0];
//dbtn = module.getElementsByTagName('button')[0];
dbtn.onclick = function(){};
*/
/*
测试2,只要函数定义不在DOM对象作用域,不会出现泄漏
*/
/*
var dbtn = module.getElementsByTagName('button')[0];
//dbtn = module.getElementsByTagName('button')[0];
dbtn.onclick = ret.show;
*/
/*
测试3,属性值内容与DOM对象间无引用,无论该DOM对象是共享对象还是私有变量,不会出现泄漏
*/
/*
var dbtn = module.getElementsByTagName('button')[0];
//dbtn = module.getElementsByTagName('button')[0];
dbtn.data = ret.data;
*/
/*
测试4,当属性值为显式的对自己的引用,无论DOM对象为私有变量还是共享对象,会出现泄漏
*/
/*
var dbtn = module.getElementsByTagName('button')[0];
//dbtn = module.getElementsByTagName('button')[0];
dbtn.data = dbtn;
*/
/*
测试5,将此DOM对象设置为另一对象的显式引用
当属性值包含function类型时,无论DOM对象为私有变量还是共享对象,会出现泄漏
*/
/*
var dbtn = module.getElementsByTagName('button')[0];
//dbtn = module.getElementsByTagName('button')[0];
dbtn.data = ret.fndata;
obtn.push(dbtn);
*/
/*
测试6,将此DOM对象设置为另一对象的显式引用
当属性值为隐性的对自己的引用,不在同一作用域下进行的赋值,无论DOM对象为私有变量还是共享对象,不会出现泄漏
*/
/*
var dbtn = module.getElementsByTagName('button')[0];
//dbtn = module.getElementsByTagName('button')[0];
dbtn.data = ret.selfdata;
obtn.push(dbtn);
*/
/*
测试7,将此DOM对象设置为另一对象的显式引用
当属性值为某对象,而该对象又包含对该DOM对象的引用,无论DOM对象为私有变量还是共享对象,会出现泄漏
*/
/*
var dbtn = module.getElementsByTagName('button')[0];
//dbtn = module.getElementsByTagName('button')[0];
dbtn.data = ret.cycledata;
obtn.push(dbtn);
*/
/*
测试8,将此DOM对象设置为另一对象的显式引用
当属性值不包含function类型时,且不存在对该DOM对象存在引用的对象,则不会出现泄漏
*/
/*
//var dbtn = module.getElementsByTagName('button')[0];
dbtn = module.getElementsByTagName('button')[0];
dbtn.data = ret.data;
obtn.push(dbtn);
*/
},
show:function(){},
fndata:{'data':function(){}},
selfdata:{'dbtn':dbtn},
cycledata:{'obtn':obtn},
data:{q:[1,2], s:'string', i:100, b:true}
};
return ret;
};
var dx = document.getElementById('x');
var my = md(dx)
var dbtny = document.getElementById('y');
dbtny.onclick = function(){
dx.innerHTML = '<button>new button '+Math.random()+'</button>';
my.init();
};
</script>
避免DOM对象泄漏的测试用例:
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<div id="x">X容器 ^_^</div>
<button id="y">重置容器X中的内容</button>
<script type="text/javascript">
var DNA = {
pool:{},
set:function(node, attr, value){
var GUID = node.getAttribute('GUID');
if(!GUID){
GUID = 'GUID'+Math.random();
node.setAttribute('GUID', GUID);
}
if(!this.pool[GUID]){
this.pool[GUID] = {};
}
this.pool[GUID][attr] = value;
},
get:function(node, attr){
var GUID = node.getAttribute('GUID');
if(!GUID){return;}
return this.pool[GUID][attr];
}
};
var dx = document.getElementById('x');
var dbtny = document.getElementById('y');
var obtn = [];
var my = {
init:function(){
var dbtn = dx.getElementsByTagName('button')[0];
obtn.push(dbtn);
DNA.set(dbtn, 'data', dbtn);
//DNA.set(dbtn, 'data', obtn);
//dbtn.data = dbtn;
}
};
dbtny.onclick = function(){
dx.innerHTML = '<button>new button '+Math.random()+'</button>';
my.init();
};
</script>
P.S. JQuery中防止内存泄漏需要确保JQuery不能被多次初始化 - -!