虽然在编写大型纯 JavaScript 项目时需要避免特定于浏览器的问题,但在使用诸如 jQuery 之类的库时,应该假定该库的设计可以帮助您避免这些问题。但是,考虑到内存泄漏很难追踪,并且特定浏览器的每个不同版本的行为可能不同 - 知道如何通常避免内存泄漏比具体要好得多:
- 如果您的代码被多次迭代,请确保您正在使用的变量可以被垃圾回收丢弃,并且不会被闭包引用绑定。
- 如果您的代码正在处理大型数据结构,请确保您有一种删除或取消数据的方法。
- 如果您的代码构造了许多对象、函数和事件侦听器 - 最好也包含一些解构代码。
- 尽量避免将 javascript 对象或函数作为属性直接附加到元素上 - 即
element.onclick = function(){}
.
- 如果有疑问,请在代码完成后进行整理。
您似乎认为调用函数的方式会对泄漏产生影响,但它总是更有可能是那些可能导致问题的函数的内容。
使用上面的代码,我唯一的建议是:
每当使用事件监听器时,都会尝试找到一种方法来重用函数,而不是为每个元素创建一个。这可以通过使用事件委托来实现(将事件 捕获在祖先/父级上并将反应委托给event.target
),或者编写一个单一的通用函数来以相对方式处理您的元素,最常见的是相对于this
or $(this)
。
当需要创建许多事件处理程序时,通常最好将这些事件侦听器存储为命名函数,以便在完成后再次删除它们。这意味着要避免像您一样使用匿名函数。但是,如果您知道它只是处理 DOM 的代码,则可以回退到 using$(elements).unbind('click')
删除所有使用 jQuery 应用于所选元素的单击处理程序(匿名或非匿名) 。但是,如果您确实使用后一种方法,那么使用 jQuery 的事件命名空间功能肯定会更好——这样您就知道您只是在删除您的事件。即$(elements).unbind('click.my_app');
。这显然意味着您必须使用绑定事件$(elements).bind('click.my_app', function(){...});
更具体:
自动调用匿名函数
(function(){
/*
running an anonymous function this way will never cause a memory
leak because memory leaks (at least the ones we have control over)
require a variable reference getting caught in memory with the
JavaScript runtime still believing that the variable is in use,
when it isn't - meaning that it never gets garbage collected.
This construction has nothing to reference it, and so will be
forgotten the second it has been evaluated.
*/
})();
使用 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.
*/
});
拆解和解构
关于我的解释,我已经将注意力集中在何时不再需要该页面并且用户正在导航离开。然而,在当今 ajaxed 内容和高度动态界面的世界中,上述内容变得更加相关;不断创建和丢弃元素的 GUI。
如果您正在创建一个动态 javascript 应用程序,我无法强调让构造函数.tearDown
或.deconstruct
在不再需要代码时执行的方法的重要性。这些应该逐步完成大型自定义对象构造并使其内容无效,以及删除已动态创建且不再使用的事件侦听器和元素。您还应该empty
在替换元素的内容之前使用 jQuery 的方法 - 这可以用他们的话更好地解释:
http://api.jquery.com/empty/
为了避免内存泄漏,jQuery 在删除元素本身之前从子元素中删除其他构造,例如数据和事件处理程序。
如果您想在不破坏其数据或事件处理程序的情况下删除元素(以便以后可以重新添加它们),请改用 .detach() 。
使用 tearDown 方法进行编码不仅会迫使您更整洁地进行编码(即确保将相关的代码、事件和元素放在一起命名空间),这通常意味着您以更加模块化的方式构建代码;对于未来的应用程序验证、可读性以及以后可能接管您项目的任何其他人来说,这显然要好得多。