I have written a js library (think, jQuery, but with much much less features, and targetted for newer browsers on mobile). This library provides a extension mechanism. One of the ways the extension can be defined is:
$.extension("extname", function(options) {
this.forEach(function(elem) {
// do something
});
return this; // for chaining.
});
This can be later used like so:
$("#myId").extname({various: "options"});
Using this mechanism I've written UI widgets (as extensions). For UI widgets that have state I don't want to have chaining like jQuery does. I want widget objects with various methods. I am aware (although I'm not too sure) that jQuery UI uses a different pattern for UI widgets.
e.g. In jQuery, if you have an accordion widget, then you have to use it like so (example only, not actual widget):
$("#acc").accordion({various: "options"}).accordion(
"collapse", 0).accordion("expand", 1); //etc.
Although I like chaining and use it in various cases, I am not particularly a fan of this style when it comes to stateful widgets. I would like widgets to be used like so:
var acc = $("#acc").accordion(options);
acc.collapse(0).expand(1);
acc.getExpanded(); // etc.
I've written a data list widget using the extension mechanism above in the following way:
(function($, undefined) {
var defaults = {
// various defaults for list widget
},
extend = $.extend,
other, variables;
// these functions don't depend or modify any state of the widget
function thoseNotDependingOnState() {
}
$.extension("datalist", function(options) {
var opts = extend({}, defaults, options),
data = [], // data for this list
self = this,
widget,
privateVars_maintaining_state;
// these functions work on private state of the widget
function someFunctionDependingOnPrivateState() {}
widget = {
getItems: function() {},
selectItemAt: function(index) {},
addItem: function(itm) {}
// etc.
};
return widget;
});
})(h5);
Since there's a chance that some widgets will be used a lot, resulting the call of the extension function, re-defining functions (private) every time a new widget is created does not make sense(?), so I'm planning to do this in the following way. Here the bulk of the behaviour is defined in the prototype. The wrapper is only a light weight object that delegates all the calls to the underlying widget and exposes only the widget's public API. The convention followed here is that all the private members of the widgets have names starting with an '_' character.
(function($, undefined) {
var defaults = {
// various defaults for list widget
},
extend = $.extend,
other, variables,
widgetProto; // This is the prototype object
// from which all the list widgets inherit.
function thoseNotDependingOnState() {
}
widgetProto = {
/* ----------------- Private variables ---------------------- */
_data: [],
_element: null, // ui element associated with this list
_allItems: [],
/* ----------------- Private functions ---------------------- */
/* Initialize the list from options passed to factory */
_init: function(element, options) {
},
_createListItem: function() {},
// other private functions
/* -------------------- Public Api --------------------------- */
getItems: function() {},
getItemAt: function(i) {},
addItem: function() {}
// other public functions
};
$.extension("datalist", function(options) {
var widget = Object.create(widgetProto), widgetWrapper = {};
// initialize the widget, passing the dom wrapper
widget._init(this, $.extend({}, defaults, options));
// prepare the wrapper
$.forEach(widget, function(val, prop) {
var property;
// expose only public API, i.e all properties
// not starting with an '_'
if(prop.indexOf("_") !== 0) {
if(typeof val === "function") {
widgetWrapper[prop] = function() {
return val.apply(widget, arguments);
}
}else {
property = capitalize(prop);
widgetWrapper["get" + property] = function() {
return widget[prop];
};
widgetWrapper["set" + property] = function(v) {
widget[prop] = v;
};
}
}
});
return widgetWrapper;
});
})(h5);
With all this, I have two questions:
Do you think its a good idea to do away with chaining pattern for widgets?
Of the above two approaches, does the second one provide any advantages in terms of performance (memory or otherwise)?
Thanks a lot in advance.
acc.collapse(0).expand(1);uses chaining. Also, you may want to read this part of Addy Osmani's book about JavaScript Patterns: addyosmani.com/resources/essentialjsdesignpatterns/book/… – Mike McCaughan Aug 15 '12 at 16:14