8
\$\begingroup\$

For non-plugin code, I generally use the module pattern, but I'm working on a jQuery plugin and decided to try out the "lightweight start" pattern. The problem is, I find myself needing $.proxy when calling private methods to ensure the this context.

Is there a better way to structure the code in order to avoid $.proxy, while ensuring the private methods have access to the init() method's this context? Where exactly should I define private properties?

Jsfiddle example without $.proxy ... also have a jsfiddle example using $.proxy

(function($, window, document, undefined) {
var pluginName = "myPlugin",
    defaults = {
        opt1: "#opt1",
    };

function Plugin(element, options) {
    this.element = $(element);
    this.options = $.extend( {}, defaults, options);
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
}

Plugin.prototype.init = function() {
    this.counter = 1;
    $("div").each(privateMethod1);
    $("body").on("click", this.options.opt1, event1);
};

function privateMethod2(i, div) {
    // "this" context is Window, not Plugin
    $(div).append(i);
    this.element.append(this.counter);        
};

function privateMethod1(i, el) {
    // "this" context is Window, not Plugin
    this.counter += 1; // undefined, but defined in init()
    privateMethod2(i, el);
};

function event1(ev, el) {
    this.counter += 1; // undefined, but defined in init()
    privateMethod1(this.counter, el);
};

$.fn[pluginName] = function(options) {
    return this.each(function() {
        if (!$.data(this, "plugin_" + pluginName)) {
            $.data(this, "plugin_" + pluginName, new Plugin(this, options));
        }
    });
};
}(jQuery));


Edit:

What I learned. This pattern appears to actually make all methods private. This is because the if (!$.data(this, "plugin_" + pluginName)) { has no else block to handle elements that already have a reference to the plugin. To allow a method to be called, check out https://stackoverflow.com/a/14128485/536734.

\$\endgroup\$
4
\$\begingroup\$

You could use a closure to create your own proxy.

Plugin.prototype.init = function() {
    var self = this;
    rows.each(function (groupIndex, group) {
        self.initGroup(groupIndex, group);
    });
    this.container.on("click", this.options.addBtnId, function (ev) {
        self.addGroup(ev);
    });
};

Why are initGroup and addGroup using $.proxy? They are already being called with the correct this context, and rebinding to the same context changes nothing.

Edit: Instead of using plain functions to implement "private methods", promote them to proper object methods and mark them private using JSDoc. This will save you those extra calls to $.proxy.

/** @private */
Plugin.prototype.initGroup = function(groupIndex, group) {
    this.addFieldIds(groupIndex, group);
};

/** @private */
Plugin.prototype.addGroup = function(ev) {
    this.initGroup(this.maxGroupIndex, newGroup);
};

In my opinion, forcing developers to jump through hoops just to hide private methods is a net loss.

\$\endgroup\$
4
  • \$\begingroup\$ I found that without $.proxy, I lost access to any variables (this.counter) created within init(). -- Should private properties be defined outside of the init() method? \$\endgroup\$
    – jbarreiros
    Mar 26 '14 at 15:59
  • 1
    \$\begingroup\$ @jbarreiros Ah, I didn't realize the private methods were plain functions and not object methods. If you want to use this inside them, you have to use something to bind the this context in the method call. $.proxy works for this, but you can instead make them proper object methods and mark them private in the documentation. See the updated fiddle. \$\endgroup\$ Mar 26 '14 at 20:49
  • \$\begingroup\$ Got it. I was under the impression that adding the function to the prototype would make it "public", but looking closer at the syntax, I realize now that you can't actually call any of the methods from outside due to the if (!$.data(this, "plugin_" + pluginName)) { conditional. \$\endgroup\$
    – jbarreiros
    Mar 27 '14 at 18:28
  • \$\begingroup\$ What is the difference between private function vs prototype function? why should not go with private functions? \$\endgroup\$
    – Jeeva Jsb
    Sep 13 '19 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.