避免闭包中的jQuery移动内存泄漏

时间:2012-01-24 19:47:58

标签: javascript jquery-plugins memory-leaks jquery-mobile closures

我正在尝试创建一个供自己使用的jQuery插件,可以使用单个命令为jQuery移动对话框设置处理程序:$('#dialog').setup_dialog({ callback: callback_function });

然而,我的处理程序由于其中的闭包而具有相当明显的内存泄漏:

$.fn.setup_dialog = function( options ) {
    var settings = $.extend({
        callback : 0
    }, options);
    var that = this;

    return this.live('pagebeforeshow', function(event, ui) {
        console.log("outside");
        $('form', that).submit( function(e) {
            var $inputs = $('form :input', that); // get all form inputs

            var values = {};
            $inputs.each(function() {
                values[this.name] = $(this).val();
            });

            that.dialog('close');

            if ( settings.callback && typeof(settings.callback) === "function" ) {
                $('#'+ui.prevPage[0].id).live('pagebeforeshow', function() {
                    settings.callback(values, that);
                    console.log("inside");
                });
            }

            return e.preventDefault();
        });
    });
}; /* setup_dialog */

如果您运行上面的代码,您会看到“内部”和“外部”首先打印一次,然后打印三次(一次提交两次),然后六次(单次提交三次),等等。 / p>

代码的目的是在jQuery Mobile对话框出现时附加一个事件处理程序,它将捕获表单提交,收集所有表单值,然后将它们传递给将修改原始页面的回调函数(启动了对话框)。

请注意,由于jQuery Mobile使用AJAX在页面之间切换的方式,我需要使用.live来绑定事件(.bind或.one将不起作用)。

我有什么想法可以避免事件积累(也可能会稍微清理一下代码)?

2 个答案:

答案 0 :(得分:0)

您正在绑定其他事件处理程序内的事件处理程序。外部事件处理程序不是唯一的事件(它们不止一次出现),这意味着您的内部事件处理程序被绑定不止一次。

一个好的解决方法是从submit事件处理程序中删除pagebeforeshow事件处理程序,并将其放在pagecreatepageinit事件处理程序中(这些是唯一的,并且会每个对话框只运行一次。)

如果你想保留当前的逻辑,那么你可以在它们运行时取消绑定事件处理程序,这样你就不会堆叠多个执行相同操作的事件处理程序:

$.fn.setup_dialog = function( options ) {
    var settings = $.extend({
        callback : 0
    }, options);
    var that = this;

    return this.live('pagebeforeshow', function(event, ui) {
        console.log("outside");
        $('form', that).submit( function(e) {
            var $inputs = $('form :input', that); // get all form inputs

            var values = {};
            $inputs.each(function() {
                values[this.name] = $(this).val();
            });

            that.dialog('close');

            if ( settings.callback && typeof(settings.callback) === "function" ) {
                $('#'+ui.prevPage[0].id).live('pagebeforeshow', function() {
                    settings.callback(values, that);
                    console.log("inside");

                    $(this).die('pagebeforeshow');//NEW, unbind the pagebeforeshow event handler for this element
                                                  //since it will be bound the next time the dialog is shown anyway
                });
            }

            $(this).die('submit');//NEW, unbind the submit event handler for this form 
                                  //since it will be bound the next time the dialog is shown anyway

            return e.preventDefault();
        });
    });
}; /* setup_dialog */

以下是.die()的文档:http://api.jquery.com/die/

答案 1 :(得分:0)

解决方案似乎如下:

$.fn.setup_dialog = function( options ) {
    var settings = $.extend({
        callback : 0
    }, options);
    var that = this; var values = {}; var submitted = 0;

    this.on('pageinit', function(event, ui) {
        $('form', that).submit(function(e) {
            var $inputs = $('form :input', that); // get all form inputs
            submitted = 1;

            $inputs.each(function() {
                values[this.name] = $(this).val();
            });

            that.dialog('close');

            return e.preventDefault();
        });

        $('.cancel-button', that).click(function() {
            submitted = 0;
        });
    });

    this.on('pagebeforehide', function(event, ui) {
        if ( submitted && settings.callback && typeof(settings.callback) === "function" ) {
            settings.callback(values, that);
        }
    });
}; /* setup_dialog */

有多种方法可以做到这一点,包括创建一个闭包并调用$('#'+ui.prevPage[0].id).die().live('pagebeforeshow', function() {

不相信这是最好的解决方案,所以我很想听到一些更好的想法。