jQuery插件中的JavaScript内存泄漏

时间:2011-12-19 16:00:09

标签: javascript jquery internet-explorer jquery-plugins memory-leaks

我有一个具有以下结构的插件:

(function($) {
    $.fn.drawFieldsTable = function(options) {
        var settings = {
                url        : 'ajax/pathToFile.php'
        };

        if (options) {
            settings = $.extend(settings, options);
        }

        return this.each(function() {
            $this = $(this);
            $.ajax({
                url  : settings.url,
                type : 'GET',
                data: // some request data here
                success: function(result) {
                    drawFieldElements(result, $this);
                }
            });

            function drawFieldElements(fields, container)
            {
                var replacement = container.clone()
                                           .empty();
                $.each(fields, function(i) {
                    var field;
                    field = buildFieldItem(i, this);
                    replacement.append(field);
                });

                container.replaceWith(replacement);
            }

            function buildFieldItem(i, fieldDataObj)
            {
                var $field = $('<div></div>')
                        .addClass('field')
                        .attr('data-fieldname', i)
                        .attr('data-ord', fieldDataObj.ord);
                return $field;
            }
        });

    };

})(jQuery);

每次buildFieldItem来电都会遇到重大内存泄漏。如果我不调用此函数或使其成为空函数或只是不在其范围内使用其参数 - 则没有泄漏。泄漏只发生在Internet Explorer(8)中。在其他浏览器中,一切都好。请求帮助。提前谢谢。

更新

我已更新了我的问题,以便更好地展示buildFieldItem实际上做了什么。 一旦fieldDataObj可能包含大约100个项目,拥有这4行只会导致喷泉,而不是IE中的泄漏。

更新2

我已经用创建一个新的div替换了clone()和empty(),我还将buildFieldItem缩小为:

function buildFieldItem(i, fieldDataObj)
{
    var $field = $('<div></div>')
            .addClass('field');
    return $field;
}

仍然在泄漏。唯一不泄漏的模式是:

function buildFieldItem(i, fieldDataObj)
{
    var $field;
    return $field;
}

看起来完全是胡说八道。功能中的代码越少 - 整体泄漏越小。可能是因为IE无法删除对整个函数的引用或像这样的smth?

2 个答案:

答案 0 :(得分:2)

既然你已经给了我们更多的buildFieldItem()函数体,我看到了另一个可能的泄漏。

如果没有处于可运行状态的完整代码,我不能肯定地说,但是当你设置$field.attr('data-ord', fieldDataObj.ord)时,如果.ord是任何类型的DOM引用,那么你可能正在存储某种DOM引用作为$ field对象的属性。当您稍后在父级上执行.empty()时,该引用可能不会在旧版本的IE中被清除,因此它会卡住并且无法收集垃圾。

这是jQuery发明.data()方法的原因之一,因为它不会在DOM对象上存储数据,并且即使在旧版本的IE上,它也会使用.empty()方法自行清理。由于我没有代码的工作版本,我不能肯定这是问题,但我建议改变:

.attr('data-ord', fieldDataObj.ord);

.data('ord',  fieldDataObj.ord);

然后,更改读取该属性的任何其他引用以使用.data()来读取它。

我也认为你在这条线上遇到麻烦,因为你依靠.clone().empty()完美地清理所有东西(甚至是jQuery没有创建的东西)和永远不会在任何浏览器中泄漏任何东西:

var replacement = container.clone().empty();

创建一个全新的容器要好得多,因为你希望它以任何方式开始,因为它不会泄漏先前的数据,因为它没有任何:

var replacement = $("<div>");

回复您对drawFieldElements()的实际内容的第三次披露:

现在,你的代码对我来说真的没有意义。你有这个:

        function drawFieldElements(fields, container)
        {
            var replacement = container.clone()
                                       .empty();
            $.each(fields, function(i) {
                var field;
                field = buildFieldItem(i, this);
                replacement.append(field);
            });

            container.replaceWith(replacement);
        }

你拿走你的容器。你克隆并清空它。所以,现在你只有一个顶级的克隆留下了一个类。您将一堆内容附加到克隆。然后,用新的克隆容器替换原始容器。你只是在使用所有这些多余的DOM操作来解决内存泄漏问题。

为什么不清除原始容器并附加到容器上?然后没有克隆,没有替换。

        function drawFieldElements(fields, container)
        {
            container.empty();
            $.each(fields, function(i) {
                container.append(buildFieldItem(i, this));
            });
        }

我不知道这是否有助于您的内存泄漏,但清理代码以删除所有不必要的DOM操作始终是朝着良好方向迈出的一步。

答案 1 :(得分:0)

所以,我终于找到了一些时间回到这个老问题并最终确定。我很快就解决了这个问题,但我一直很忙。

以下行导致IE中的内存泄漏:

var $isObligatory = $('<input>')
    .attr('type', 'checkbox')
    .attr('name', 'is-obligatory')
    .prop('checked', isObligatory)
    .change(function() {
        settings.gFields.applied[settings.subsection][i].obligate =
            ($(this).prop('checked')) ? 1 : 0;
    });

正如您所看到的,它在为change事件添加处理程序时会创建某种循环引用。参考IE不够聪明,无法破解。

事实是,对此进行评论是我的第一次尝试,但是一旦我在同一个文件中实现了可拖动的功能,泄漏仍然存在,因为可拖动也导致它!这就是为什么我在努力解决这个问题。在插件代码之外移动draggables解决了它。

感谢大家发表评论和回答。我很抱歉没有发布完整的代码,我认为我足够聪明,只发布有问题的行。对不起,迟到了。