首先,我在这个问题上进行了广泛的阅读和研究。我正在关注jquery UI的票证。我知道解决这个问题的方法,但我很好奇为什么会这样。我相信这个错误是由于关闭,但我的javascript-fu不是专家。
我认为jquery UI团队有更好的事情要做,而不是花费精力来处理IE6错误。所以我想把它带到一般的javascript public。
以下是测试用例:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>jquery ui memory leak test</title>
<script type="text/javascript" src="jquery-1.5.js"></script>
<script type="text/javascript" src="jquery.ui.widget.js"></script>
<script type="text/javascript">
(function($) {
$.widget("ui.test", {
_create: function() {
}
});
$(document).ready(function() {
for (var i = 0; i < 1; i++) {
$("#container").append("<div id='inner'></div>");
$("#inner").test();
$("#inner").test("destroy");
$("#container").empty();
};
});
})(jQuery);
</script>
</head>
<body>
<div id="container">
</div>
</body>
</html>
我已经使用jquery 1.4.4和1.5以及jquery-ui-1.8.9和jquery-ui master(写作时)的所有组合进行了测试,但它们都产生了相同的结果。
我认为我的测试小部件是最简单的。
如果使用sIEve进行测试,则可以发现泄漏。否则,将计数器增加到1000,你会很容易看到内存增加。您可以使用另一个来自Microsoft的tool来检测泄漏。
因此泄漏是由于小部件的_createWidget
方法中的自定义事件绑定所致:
var self = this;
this.element.bind( "remove." + this.widgetName, function() {
self.destroy();
});
所以,如果我发表评论,那就没有泄密。我使用1.8.9而不是master,因为1.8.9的widget代码看起来更简单(master已经改变了一点)。
现在,如果我完全将小部件之外的相同事件绑定在一起,那么也没有泄漏。例如,我在创建小部件之后但在destroy之前插入以下代码:
$("#inner").bind("remove.test", function() {});
我故意添加了一个无操作函数,但回调函数中的内容并不重要。你可以争辩说,因为我之后手动破坏,所以不需要绑定。但这不是重点。
所以我的问题是为什么原始代码,小部件代码中的绑定调用会泄漏?我怀疑这是因为关闭,但我无法解释。
有人可以解释一下吗?
答案 0 :(得分:2)
据我所知,当JS变量指向DOM对象时,JS和DOM之间存在循环引用,并且该DOM对象具有指向返回的属性(或通常是事件处理程序)时,问题就会发生JS变量。上面的.bind()示例似乎是这样做的。显然,IE在其垃圾收集过程中使用引用计数,并且不会收集循环引用。
我见过至少有几个MSDN博客将此归咎于JavaScript并且基本上建议避免关闭,这显然不是很有帮助,但这里有一些非Microsoft / MSDN文章讨论问题并提供一些解决方法:
http://laurens.vd.oever.nl/weblog/items2005/closures/
答案 1 :(得分:0)
对不起,我无法解释此问题的原因,但我采取了一种解决方法,以防止发生这种相当恼人的泄漏。
在我的页面中包含jquery和jquery-ui后,我添加了以下代码:
var origCreateWidget = $.Widget.prototype._createWidget;
$.Widget.prototype._createWidget = function( options, element ) {
var origBind = $.fn.bind;
var widget = this;
$.fn.bind = function( type, func ) {
if( typeof( type ) === "string" && type.indexOf( "remove." ) === 0) {
// ignore the remove events
}
else {
origBind.apply( this, arguments );
}
return this;
}
var res = origCreateWidget.call( this, options, element );
$.fn.bind = origBind;
return res;
};
此外,当窗口卸载事件发生时,我调用destroy
。在这些更改之后,sIEve不报告任何泄漏,并且Windows任务管理器在浏览包含可排序小部件的页面时显示常量内存消耗。
经过数小时的搜索和测试后,我仍然无法找出泄漏的真正原因。