patternjavascriptMinor
Pattern for creating a globally accessible custom plugin
Viewed 0 times
globallycreatingaccessiblecustompluginforpattern
Problem
I'm currently using this pattern to create and extend a globally accessible custom plugin to be used in a web application:
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
In my mind, this syntax would be nice:
Is this a good idea? If not, why? What would be a better approach?
To implement this, I would do
Is there a more convenient way to add many properties to
For example, one that would not require one statement per property to be added. Should I use
; // 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 driveIs 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?Solution
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:
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
where
Now how to implement?
Well, each of your functions should call a special internal function that fires callbacks collected in your library. Basically, the
(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();Code Snippets
(function(ns){
//private scoping
var privVar = '...';
function priv(){...}
//expose a function
ns.myFunc = function(){...};
}(this.myLib = this.myLib || {}));
//using the exposed myFunc
myLib.myFunc();myLib.on('fnName',function(){...});(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();Context
StackExchange Code Review Q#21105, answer score: 9
Revisions (0)
No revisions yet.