Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm currently using this pattern to create and extend a globally accessible custom plugin to be used in a web application:

; // defensive programming: script may be concatenated with others

(function($) {
    "use strict";

    // allow Plugin to be extended from other scripts
    var window.Plugin = window.Plugin || {};

    var privateVars = {
        // this contains all of the plugin's "private static" fields
    };

    function someHelperFunction() {
        // this is a private method
    }

    $.extend(window.Plugin,
    {
        method1: function() {
            // this is a public method
        },
        method2: function() {
            // this is a public method
        }
    });
})(jQuery);

This approach looks fine to me and seems to tick all the boxes. However, since I'm far from a JS guru I wonder:

Are there any issues with the above pattern that I should be aware of?

Now suppose I want to introduce global configuration that affects the behavior of all subsequent calls to method1, for example allow the user to register a callback that will get fired every time before method1 executes.

In my mind, this syntax would be nice:

window.Plugin.method1.registerCallback(function() { ... });
window.Plugin.method1(); // take it for a test drive

Is this a good idea? If not, why? What would be a better approach?

To implement this, I would do

window.Plugin = { ... }; // as above
window.Plugin.method1.registerCallback = function() {
    // this would write something inside privateVars
};

Is there a more convenient way to add many properties to method1 like this?

For example, one that would not require one statement per property to be added. Should I use jQuery.extend for this as well?

share|improve this question

1 Answer 1

up vote 4 down vote accepted

The usual way to create a plugin is to declare a namespace or use an existing and append to it. Usually done using the module pattern:

(function(ns){

  //private scoping
  var privVar = '...';
  function priv(){...}

  //expose a function
  ns.myFunc = function(){...};

}(this.myLib = this.myLib || {}));

//using the exposed myFunc
myLib.myFunc();

Issues with your code? Well, it depended on jQuery just to append methods. It's not really necessary to use extend. With the above pattern, you can cleanly add functions to the namespace.

If you really need just some parts of jQuery, you can grab their code and just use it directly. I usually interrogate the code and pick out only the parts that are necessary. Or you can roll your own jQuery build. They have instructions on their GitHub on how to roll your own mix of jQuery.

Now, for function callbacks, you should use the a technique similar to pub-sub or event emitters or jQuery's on method. The syntax looks like this:

myLib.on('fnName',function(){...});

where fnName is the function name that is equivalent to the function called.

Now how to implement?

Well, each of your functions should call a special internal function that fires callbacks collected in your library. Basically, the on, method of your library just collects callbacks and registers them in an internal cache id'ed with the name of the function. The implementation would look like this:

(function(){

  //handler cache
  var handlers = {};

  //our internal function that executes handlers with a given name
  function executeHandlers(eventName){
    //get all handlers with the selected name
    var handler = handlers[eventName] || []
      , len = handler.length
      , i
      ;
    //execute each
    for(i = 0; i< len; i++){
      //you can use apply to specify what "this" and parameters the callback gets
      handler[i].apply(this,[]);
    }
  }

  //our on function which collects handlers
  ns.on = function(eventName,handler){
    //if no handler collection exists, create one
    if(!handlers[eventName]){
      handlers[eventName] = [];
    }
    handlers[eventName].push(handler);
  }

  //so we expose a shout function
  ns.shout = function(){

    ...stuff shout does...

    //now we execute callbacks registered to shout
    executeHandlers('shout');
  }

}(this.myLib = this.myLib || {}));

//we register a callback
myLib.on('shout',function(){
  //executed after calling shout
});

//execute shout
myLib.shout();
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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