目前大多数ajax前端的javascript framework都利用对事件的管理,解决了该问题。
如果你需要自己解决这个问题,可以参考以下的一些方法:
-
可以利用递归Dom树,解除event绑定,从而解除循环引用:
if (window.attachEvent) { var clearElementProps = [ 'data', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'ondblclick', 'onclick', 'onselectstart', 'oncontextmenu' ]; window.attachEvent("onunload", function() { var el; for(var d = document.all.length;d--;){ el = document.all[d]; for(var c = clearElementProps.length;c--;){ el[clearElementProps[c]] = null; } } }); }
-
而http://novemberborn.net/javascript/event-cache
一文中则通过增加EventCache,从而给出一个相对结构化的解决方案
-
/* EventCache Version 1.0
Copyright 2005 Mark Wubben
Provides a way for automagically removing events from nodes and thus preventing memory leakage.
See <http://novemberborn.net/javascript/event-cache> for more information.
This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
*/
/* Implement array.push for browsers which don't support it natively.
Please remove this if it's already in other code */
if (Array.prototype.push == null ){
Array.prototype.push = function (){
for ( var i = 0 ; i < arguments.length; i ++ ){
this [ this .length] = arguments[i];
};
return this .length;
};
};
/* Event Cache uses an anonymous function to create a hidden scope chain.
This is to prevent scoping issues. */
var EventCache = function (){
var listEvents = [];
return {
listEvents : listEvents,
add : function (node, sEventName, fHandler, bCapture){
listEvents.push(arguments);
},
flush : function (){
var i, item;
for (i = listEvents.length - 1 ; i >= 0 ; i = i - 1 ){
item = listEvents[i];
if (item[ 0 ].removeEventListener){
item[ 0 ].removeEventListener(item[ 1 ], item[ 2 ], item[ 3 ]);
};
/* From this point on we need the event names to be prefixed with 'on" */
if (item[ 1 ].substring( 0 , 2 ) != " on " ){
item[ 1 ] = " on " + item[ 1 ];
};
if (item[ 0 ].detachEvent){
item[ 0 ].detachEvent(item[ 1 ], item[ 2 ]);
};
item[ 0 ][item[ 1 ]] = null ;
};
}
};
}();
-
使用方法也很简单:
<script type="text/javascript">
function addEvent(oEventTarget, sEventType, fDest){ if(oEventTarget.attachEvent){ oEventTarget.attachEvent("on" + sEventType, fDest); } elseif(oEventTarget.addEventListener){ oEventTarget.addEventListener(sEventType, fDest, true); } elseif(typeof oEventTarget[sEventType] == "function"){ var fOld = oEventTarget[sEventType]; oEventTarget[sEventType] = function(e){ fOld(e); fDest(e); }; } else { oEventTarget[sEventType] = fDest; }; /* Implementing EventCache for all event systems */ EventCache.add(oEventTarget, sEventType, fDest, true); }; function createLeak(){ var body = document.body; function someHandler(){
return body;
};
addEvent(body, "click", someHandler);
};
window.onload = function(){
var i = 500;
while(i > 0){
createLeak();
i = i - 1;
}
};
window.onunload = EventCache.flush;
</script>
-
http://talideon.com/weblog/2005/03/js-memory-leaks.cfm
一文中的方法类似:
-
/*
* EventManager.js
* by Keith Gaughan
*
* This allows event handlers to be registered unobtrusively, and cleans
* them up on unload to prevent memory leaks.
*
* Copyright (c) Keith Gaughan, 2005.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* (CPL) which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/cpl.php
*
* This software is covered by a modified version of the Common Public License
* (CPL), where Keith Gaughan is the Agreement Steward, and the licensing
* agreement is covered by the laws of the Republic of Ireland.
*/
// For implementations that don't include the push() methods for arrays.
if ( ! Array.prototype.push) {
Array.prototype.push = function (elem) {
this [ this .length] = elem;
}
}
var EventManager = {
_registry: null ,
Initialise: function () {
if ( this ._registry == null ) {
this ._registry = [];
// Register the cleanup handler on page unload.
EventManager.Add(window, " unload " , this .CleanUp);
}
},
/* *
* Registers an event and handler with the manager.
*
* @param obj Object handler will be attached to.
* @param type Name of event handler responds to.
* @param fn Handler function.
* @param useCapture Use event capture. False by default.
* If you don't understand this, ignore it.
*
* @return True if handler registered, else false.
*/
Add: function (obj, type, fn, useCapture) {
this .Initialise();
// If a string was passed in, it's an id.
if ( typeof obj == " string " ) {
obj = document.getElementById(obj);
}
if (obj == null || fn == null ) {
return false ;
}
// Mozilla/W3C listeners?
if (obj.addEventListener) {
obj.addEventListener(type, fn, useCapture);
this ._registry.push({obj: obj, type: type, fn: fn, useCapture: useCapture});
return true ;
}
// IE-style listeners?
if (obj.attachEvent && obj.attachEvent( " on " + type, fn)) {
this ._registry.push({obj: obj, type: type, fn: fn, useCapture: false });
return true ;
}
return false ;
},
/* *
* Cleans up all the registered event handlers.
*/
CleanUp: function () {
for ( var i = 0 ; i < EventManager._registry.length; i ++ ) {
with (EventManager._registry[i]) {
// Mozilla/W3C listeners?
if (obj.removeEventListener) {
obj.removeEventListener(type, fn, useCapture);
}
// IE-style listeners?
else if (obj.detachEvent) {
obj.detachEvent( " on " + type, fn);
}
}
}
// Kill off the registry itself to get rid of the last remaining
// references.
EventManager._registry = null ;
}
};
-
使用起来也很简单
<html>
<head> <script type=text/javascript src=EventManager.js></script> <script type=text/javascript> function onLoad() { EventManager.Add(document.getElementById(testCase),click,hit ); returntrue; } function hit(evt) { alert(click); } </script> </head> <body οnlοad='javascript: onLoad();'> <div id='testCase' style='width:100%; height: 100%; background-color: yellow;'> <h1>Click me!</h1> </div> </body> </html>
-
- google map api同样提供了一个类似的函数用在页面的unload事件中,解决Closure带来的内存泄露问题。
-
当然,如果你不嫌麻烦,你也可以为每个和native object有关的就阿vascript object编写一个destoryMemory函数,用来手动调用,从而手动解除Dom对象的事件绑定。
-
还有一种就是不要那么OO,抛弃Dom的一些特性,用innerHTML代替appendChild,避开循环引用。详细见http://birdshome.cnblogs.com/archive/2005/02/16/104967.html
中的讨论贴。\
其它一些琐碎的注意点
变量定义一定要用var,否则隐式声明出来的变量都是全局变量,不是局部变量;
全局变量没用时记得要置null;
注意正确使用delete,删除没用的一些函数属性;
注意正确使用try...cache,确保去处无效引用的代码能被正确执行;
open出来的窗口即使close了,它的window对象还是存在的,要记得删除引用;
frame和iframe的情况和窗口的情况类似。
-