How Do You Encapsulate Your JavaScript
I ask this question a lot! To Job candidates, friends, and almost any developer that says they work with JavaScript. I believe how you encapsulate your JavaScript is a good indicator on your level of expertise with the language. I find that most beginners have come to JavaScript via jQuery and often define their functions at the top level namespace in some application.js file. These functions are loosely organized and often have no way of sharing simple object state and behavior. A good object oriented programmer would never write their application code like that. JavaScript should be no different and I think jQuery has a part in the blame of those not knowing this.
I've been writing rich object oriented JavaScript for the better part of 4 years now using Prototype.js. That framework has always championed encapsulating your web application's behavior using a standard class and inheritance notation. Sam Stephenson, the author of Prototype.js, did a good job trying to educate what real JavaScript Object Notation (JSON) is about and how you can encapsulate behavior using Prototype's Class structure while paying homage to Dean Edward's Base.js to which it owes its origins.
So jQuery is the proper winner and choice for anyone wanting to use a JavaScript framework to make working with the DOM in web sites easier. It has awesome event handling and delegation. So many things that Prototype.js lacked. It even has great documentation. About the only bad thing about jQuery is the widely different interfaces to it's AJAX functions and the arguments passed to their callbacks. Oh, and they totally screwed new JavaScript developers by not sheperding them into learning some sort of way to encapsulate their object behavior. So this article is for those that are not using CoffeeScript's class system, Backbone.js's class system, or jQuery's deeply nested plugin class system that is hidden away. So lets get to fixing that. Here is 32 lines of dirt simple JavaScript inheritance.
/* Simple JavaScript Class
By John Resig. MIT Licensed. <http://ejohn.org/blog/simple-javascript-inheritance/> */
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
this.JQClass = function(){};
JQClass.extend = function(prop) {
var _super = this.prototype;
initializing = true;
var prototype = new this();
initializing = false;
for (var name in prop) {
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
function JQClass() {
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
JQClass.prototype = prototype;
JQClass.prototype.constructor = JQClass;
JQClass.extend = arguments.callee;
return JQClass;
};
})();
It is worth reading the comments John Resig's blog post for this piece of work as well as the full documentation. My code example above names this JQClass
to avoid namespace collisions with other frameworks. Here is a simple example of its usage.
// Your namespace.
var MyApp = {
models: {}
};
// Simple object example for page flash.
MyApp.models.flash = JQClass.extend({
init: function(aPage) {
this.page = $(aPage);
this.alertDiv = this.page.find('div.flash.alert');
this.noticeDiv = this.page.find('div.flash.notice');
},
alert: function(content) { this._doFlashFor(this.alertDiv,content); },
notice: function(content) { this._doFlashFor(this.noticeDiv,content); },
clear: function() {
# ...
},
_doFlashFor: function(flash, content) {
$.mobile.silentScroll(0);
this.clear();
flash.html(content);
flash.show();
}
});
This code example is pulled from a jQuery mobile project I recently finished. The real usage of the flash objects would be numerous as there would be one per page and I have a top level delegate object that finds the active page and finds or create the flash DOM elements. But this should be a good enough example to show why you might choose a simple class and object inheritance system vs putting all your logic in $(document).ready({})
scopes. There are tons of ways to write good OO JavaScript and I hope some find this useful and explore some other ways.