虽然编写大型纯JavaScript项目时需要避免特定于浏览器的问题,但使用jQuery等库时应该假定库的设计可以帮助您避免这些问题。然而,考虑到内存泄漏很难跟踪,并且每个特定浏览器的不同版本可能会有所不同,因此最好知道如何通常避免内存泄漏而不是具体地指出:
- 如果您的代码被迭代多次,请确保您正在使用的变量可以通过垃圾回收进行丢弃,并且没有被封闭引用占用。
- 如果您的代码处理大型数据结构,请确保您有一种方法来删除或使数据无效。
- 如果您的代码构造了许多对象、函数和事件监听器,则最好也包括一些解构代码。
- 尽量避免直接将JavaScript对象或函数附加到元素作为属性 - 即
element.onclick = function(){}
。
- 如有疑问,请在代码完成后进行整理。
您似乎认为调用函数的方式会影响泄漏,但实际上更有可能是这些函数的内容会导致问题。
对于您上面的代码,我的唯一建议是:
每当使用事件监听器时,请尝试找到一种重用函数的方法,而不是为每个元素创建一个函数。这可以通过使用事件委托(在祖先/父级上捕获事件并将反应委托给event.target
)或编写一个通用的单一函数来处理您的元素,通常相对于this
或$(this)
。
当需要创建许多事件处理程序时,通常最好将这些事件监听器存储为命名函数,以便在完成后可以再次删除它们。这意味着避免使用匿名函数,就像您正在做的那样。但是,如果您知道只有您的代码处理DOM,则可以回退到使用$(elements).unbind('click')
来删除应用于所选元素的所有单击处理程序(无论是否为匿名)使用jQuery。然而,如果使用后一种方法,则绝对最好使用jQuery的事件命名空间功能 - 这样您就知道只删除自己的事件。即$(elements).unbind('click.my_app');
。这显然意味着您必须使用$(elements).bind('click.my_app', function(){...});
绑定事件。
更具体地说:
自动调用匿名函数
(function(){
})();
adding an anonymous event listener with jQuery:
var really_large_variable = {/*Imagine lots of data here*/};
$(element).click(function(){
/*
Whilst I will admit not having investigated to see how jQuery
handles its event listeners onunload, I doubt if it is auto-
matically unbinding them. This is because for most code they
wont cause a problem, especially if only a few are in use. For
larger projects though it is a good idea to create some beforeunload
or unload handlers that delete data and unbind any event handling.
The reason for this is not to protect against the reference of the
function itself, but to make sure the references the function keeps
alive are removed. This is all down to how JS scope works, if you
have never read up on JavaScript scope... I suggest you do so.
As an example however, this anonymous function has access to the
`really_large_variable` above - and will prevent any garbage collection
system from deleting the data contained in `really_large_variable`
even if this function or any other code never makes use of it.
When the page unloads you would hope that the browser would be able
to know to clear the memory involved, but you can't be 100% certain
it will *(especially the likes of IE6/7)* - so it is always best
to either make sure you set the contents of `really_large_variable` to null
or make sure you remove your references to your closures/event listeners.
*/
});
tearDowns 和清理
在我的解释中,我主要关注的是当页面不再需要并且用户正在导航离开时。然而,在今天的Ajax内容和高度动态交互界面的世界中,上述问题变得更加重要;GUI不断地创建并销毁元素。
如果您正在创建一个动态的JavaScript应用程序,我强烈建议您使用具有 .tearDown
或 .deconstruct
方法的构造函数,这些方法在代码不再需要时执行。这些方法应该遍历大型自定义对象结构并将其内容变为空,同时删除已经动态创建并且不再使用的事件监听器和元素。在替换元素的内容之前,您还应该使用 jQuery 的 empty
方法 - 这可以从他们的说明中更好地解释:
http://api.jquery.com/empty/
为了避免内存泄漏,jQuery会从子元素中删除数据和事件处理程序等其他构造物,然后再删除元素本身。
如果您想删除元素,而不破坏它们的数据或事件处理程序(以便稍后可以重新添加),请改用 .detach()。
编写具有 tearDown 方法的代码不仅迫使您更加整洁地编写代码(即确保将相关的代码、事件和元素命名空间在一起),而且通常意味着您以更模块化的方式构建代码;这显然对于提高应用程序的未来可靠性、可读性以及任何可能在以后接手项目的人都有很大的好处。
hideWrapper()
周围加上try/catch? - nnnnnn