我最近一直在阅读有关内存泄漏的内容,并且尚未解开所有内容并对我自己的写作风格提出一些疑问。具体来说,我不确定我处理事件的方式是否可能是泄漏的来源。请考虑以下代码
function Wrapper(text) {
this.text = text;
this.bindHandlers();
};
Wrapper.prototype.onClick = function (e) {
alert(this.text);
};
Wrapper.prototype.bindHandlers = function () {
var t = this, div = $('<div>' + this.text + '</div>');
var reallyHugeArray = [1,2,3...]; // an array of 100000 elements for example
div.click(function (e) {
// all variables of the parent function are in scope for this function, including reallyHugeArray
t.onClick(e);
});
$(document).append(div);
};
var a = new Wrapper('testString');
// had enough fun with the Wrapper, now let's nullify it
a = null;
如您所见,我喜欢使用匿名函数作为事件处理程序,以便更方便地访问实例特定变量(在本例中this.text
函数中onClick
)和功能。但是,如果我理解正确,在函数内部(如事件处理程序)中有一个匿名函数,它可以访问本地作用域,禁止垃圾收集器删除局部变量,从而产生泄漏。
所以我的问题是这种事件处理方法是否会造成内存泄漏,如果有,是否有任何方法可以阻止它,但仍然有一种类似方便的方式来访问实例变量和函数?
(偏离主题:函数内部函数内部的函数使Javascript听起来像Inception)
答案 0 :(得分:1)
在您的特定示例中,匿名单击处理程序为其上方的作用域创建一个函数闭包。这意味着t
,div
和reallyHugeArray
的值会在您的匿名点击处理函数的生命周期内得到维护。
这不是真正的记忆“泄漏”,而是记忆“使用”。随着时间的推移,它不会变得越来越糟,它只使用那些本地变量t
,div
和reallyHugeArray
占用的固定内存量。这通常是javascript编程的一个优点,因为这些变量可用于内部函数。但是,正如您所想的那样,如果您希望释放内存,它偶尔会导致问题。
在引用其他内容(DOM对象或其他JS变量)的情况下,由于这些外部变量继续存在,它们引用的所有内容也会继续,并且不能被垃圾收集器释放。一般来说,这不是一个大问题。容易引起问题的事情是在使用网页时反复完成的事情,或者在具有大量迭代的大型循环中完成的事情。像这样只执行一次的东西只使用了一点内存,从那时起构造的内存使用量就不变了。
如果由于某种原因,你一遍又一遍地绑定这个事件处理程序,每次都创建一个新的函数闭包而从不释放它们,那么这可能是个问题。
我在Javascript中发现这个构造非常有用。我不认为它是远离它的东西,但是如果你引用了你想要被释放的非常大的东西,应该释放的短暂的东西,因为你不需要长期的东西,这是值得理解的。或者你一遍又一遍地做着什么。在这种情况下,如果你不需要在内部函数中使用它们来杀死它们的引用并允许垃圾收集器执行它,你可以显式地将局部变量设置为null
。但是,这通常不是您通常需要做的事情 - 在某些情况下需要注意的事情。