jQuery扩展和闭包

时间:2011-09-08 16:49:28

标签: javascript jquery closures

我正在学习javascript,并且正在努力学习更多有关jQuery的知识。我在JS中创建了一个非常简单的“表单控制器”,所以当我创建传递表单作为参数的对象时,它会将事件连接起来并劫持提交:

var FormController = function (form) {

    // private field
    var _form = $(form);
    var _url = _form.attr('action');
    var _isValid = false;

    $(form).submit(function (e) {
        submitForm();
        e.preventDefault();
    });

    var disableAll = function () {
        _form.find('select,input,textarea').attr('disabled', 'disabled')
    };

    var enableAll = function () {
        _form.find('select,input,textarea').removeAttr('disabled')
    };

    var submitForm = function () {

        disableAll();
        _isValid = true;

        if (_isValid) {
            $.ajax({
                url: _url,
                type: 'POST',
                success: function (data, textStatus, jqXHR) {
                    enableAll();
                    var obj = jQuery.parseJSON(jqXHR.responseText);
                    print(data.Result ? data.Result : "[No Result]");
                    print(textStatus.toString());
                },
                error: function (data, textStatus, jqXHR) {
                    print(textStatus);
                    _form.css('border', '1px solid Red');
                    enableAll();
                }
            });
        }
    };

    // public fields and functions
    return {
        getIsValid: function () { return _isValid; },
        submit: function () { return submitForm(); }
    };
};

它按预期工作。现在我想创建一个jQuery扩展,所以我可以将该对象应用于结果:

$.fn.formController = function () {
    return this.each(function (i, item) {
        new FormController(item);
    });
};

我也按预期工作,但...... GC会收集该对象吗?或者因为事件是有线的,它计为引用?

现在,我想保持控制器的实例可用,所以我可以在创建后操作它们,例如调用方法。像这样:

$('#myForm').formController('submit');

我尝试了几种方法,但我无法得到它。我试图用一个跟踪物品等的物品放置一个封闭物......但我只是弄乱了“这个”。

这样做的正确方法是什么?而且我认为到目前为止我所做的一切都可能是错的,即使它有效。

感谢。

更新

很抱歉,如果问题不够明确。我的目标是我可以这样做:$('#myForm').formController('submit');,然后函数将找到与该HTML对象关联的“FormController”对象,并调用此“提交”成员。

我想实现这样的目标:

$.fn.formController = function (name) {

    if(!document._items)
        document._items = [];

    if (name) {
        return this.each(function (i, item) {
            var id = $(item).data('form-controller');

            if (id) {
                var fc = document._items[id];
                var member = fc[name];
                if (member) {
                    if (typeof member == 'function')
                        member();
                }
            }
        });
    }
    else {    
        return this.each(function (i, item) {
            var id = document._items.push(new FormController(item)) - 1;
            $(item).data('form-controller', id.toString());
        });
    }
};

这种方法的问题在于我将一个集合作为全局对象,我想要的是将其作为内部对象。我尝试过一个闭包,但我只遇到了“this”(指向DOMwindow)或“_items”var为空的问题。

采用这种方法的正确方法是什么?

3 个答案:

答案 0 :(得分:2)

您可以使用jQuery UI的Widget工厂,它可以为您提供开箱即用的功能:

$.widget("vtortola.formcontroller", {
    _create: function() {
         // Use `this.element` to initialize the element.
         // You can also use this.options to get the options object that was passed.
    },
    _destroy: function() {
        // You can unbind events here to remove references from the DOM, so is'll get
        // deleted by the garbage collector.
    },
    submit: function() { /* ... */ }
});

// Example
$('#form').formcontroller({foo: 123}); // Calls `_create()` with the supplied options
$('#form').formcontroller('submit'); // Calls the `submit` method

小部件工厂还为您提供选项设置器/ getter,默认选项和其他很酷的东西。有关详细信息,请参阅the official docstutorial at bililite。如果你没有将jQuery UI用于其他东西,你可以使用widget factory code作为独立的,它不依赖于jQuery UI的其他部分。

如果你不喜欢使用jQuery UI的widget工厂,你可以将对象直接存储在.data()上(这也是widget工厂的工作),不需要保留它们的集合并跟踪手动。你的代码将是:

$.fn.formController = function () {
    var args = arguments;
    return this.each(function (i, item) {
        var $this = $(this), _obj;
        // When calling `$(..)`.formController() with arguments when the object already
        // exists, it treats the first argument as the method name and passes the rest
        // as arguments to that method.
        if (arguments.length && (_obj = $this.data('FormController'))) {
            _obj[args[0]].apply(_obj, Array.prototype.slice.call(args, 1));
        } else {
            $this.data('FormController', new FormController(item));
        }
    });
};

答案 1 :(得分:0)

使用

(function($){
    $.fn.formController = function(parameter1, parameter2){
            //this = #myForm
    }
)(jQuery);

并阅读this

答案 2 :(得分:0)

我还尝试创建一个集合作为函数本身的属性。

它有效,但我不知道它是多么正确/恰当:D

$.fn.formController = function (name) {

    if (name) {
        return this.each(function (i, item) {
            var id = $(item).data('form-controller');

            if (id) {
                var fc = $.fn.formController.Items[id];
                print('get: ' + id + ' ' + (fc?'found':'not found'));
                var member = fc[name];
                if (member) {
                    if (typeof member == 'function')
                        member();
                }
            }
        });
    }
    else {

        return this.each(function (i, item) {
            var id = $.fn.formController.Items.push(new FormController(item)) - 1;
            print('add: ' + id);
            $(item).data('form-controller', id.toString());
        });
    }
};
$.fn.formController.Items = [];