Pondering Open Source JavaScript OOP (16 October 2009)
NOTE: A working example of what this article discusses may be found here.
With the creation of JavaScript SDKs for mobile devices, the JavaScript programming language may soon become the language of choice for those seeking to rapidly develop complex algorithms for porting to other environments.
More powerful languages require minutes if not hours of compiling, linking, and other standards to test new changes. With standard JavaScript, many of these changes can be tested in mere seconds and then migrated to the target code base.
Having taught myself HTML, CSS, and JavaScript, there are many features I only become aware of surreptitiously. Upon the discovery of neat tricks to try, I like to incorporate them into my personal coding repertoire.
One such example is applying XHTML rules to my HTML practices. I don't know all the ins and outs of XHTML, but I have started closing all my open (and lower-case) tags while flagging tags that have no partner with a slash, wrapping HTML parameters with quotes, etc. I have also started making liberal use of semicolons even in cases where they're optional, as in the following:
There are also some requirements for DHTML that aren't necessary in pure HTML that need consideration such as prepending a 'thead' element to 'tbody' elements when dynamically creating tables.
For my JavaScript code, I try to program as much in Object Oriented Programming style as I can. The literature makes it clear the JavaScript isn't a true OOP language, but developers have worked wonders trying to overcome that limitation.
I can't tell you how ecstatic I was to run across Dean Edwards' Open Source Base.js class object which allows inheritance. It also introduced me to a new way to declare objects. An explanation of his work may be found at:
Reading over Dean's page again, I'm realizing just how much of his base class functionality I'm not currently using in code I write.
Another JavaScript technique I was unfamiliar with involved optional arguments, and for a long time I simply didn't need them. Eventually, though, my web page scripting pursuits required optional arguments, and with a little search engine help I found the article "Optional Function Arguments in JavaScript" at:
Suddenly realizing there's a default 'arguments' array passed to a function opened a lot of doors. I was now able to write something like:
var Point = Base.extend({
x:0,
y:0,
constructor: function()
{
if (typeof arguments[0] != 'undefined')
{
if (typeof arguments[1] != 'undefined')
{ // x,y
this.x = arguments[0];
this.y = arguments[1];
}
else
{ // Point
this.x = arguments[0].x;
this.y = arguments[0].y;
}
}
}
});
Being able to call new Point(), Point(pt), and Point(x,y) has come in in very handy.
One thing I find most intriguing about the article above is the section on Associative Arrays as optional parameters. I needed a means to assign values to properties shared by like objects, so I created something along the lines of:
Note that the custom function getUniqueId() used above and declared below implements a common practice to generate a unique identification number by creating a global variable set to 0 and incrementing it by the function call. A little known JavaScript ability instead obfuscates that id into series of function calls, as in:
var getUniqueId =
(function(){var id=0;return function(){if(arguments[0]===0)id=0;
return id++;}})();
I don't exactly understand how all the function returning an internal function as a function of some function works in its entirety -- hardly anyone does or this technique would be far more common -- but I did figure out how to include a check for an optional parameter, in this case the Number 0, to reset the id if necessary.
Also, for those unfamiliar with using three consecutive equal sign characters, the Boolean combination indicates that not only does the value have to equal 0, but that it must be of the same Number type.
The check against a Number isn't necessary as the simple double equal sign == check against "0" would do or even a -1 Number, but I thought I'd include the explicit comparison here since seeing === used the first time stumped me for a while thinking it a typo, and adding the reset ability prompted one coder to comment back to me being careful resetting ids...plus it's surely faster.
Make parseInt(String) your rival enemy.
With BaseName declared as just a blip on my radar at this point, a BaseItem can be derived from it that adds an array of default properties:
Note that the 'property' method will set the property if supplied a second parameter.
Client side presentation of the object may then use the following:
var ClientItem = BaseItem.extend({
constructor: function(name,default_properties)
{
this.base(name,default_properties); // call BaseItem.constructor
},
addProperties: function(props)
{
for (var i in this.properties) // label i
if (typeof props[i]=='undefined')
props[i]=this.properties[i];
return this.properties=props;
}
});
The objects above would reside in some intermediary .js header file referenced by the specific client code. The specific implementation, in this case for a calculator I'm using the game Mafia Wars for, may then read:
var MafiaWars_DefaultJobItem = ClientItem.extend({
constructor: function(name)
{
this.base(name,{'region':"", 'rank':"", 'energy':1, experience':0,
'requires':null, 'drops':null, 'money':0});
if (typeof arguments[1]!='undefined')
this.addProperties(arguments[1]);
}
});
The object above creates a ClientItem, supplying the defaults required by the MafiaWars_DefaultJobItem through the call to the Base.js 'base' method which calls up the tree. In this case, calling base() will reference its constructor's 'constructor' function.
Note that assigning 'energy' the value 1 is just a reminder not to divide by zero and that any property label array may be added via the addProperties method.
The MafiaWars_DefaultJobItem at this point is still too cumbersome for practical use but I may derive another object allowing it to take as parameters the three most common properties I need at this time I can readily access: name, energy, and experience. Think of it as a debugging stage.
var MafiaWars_Job = MafiaWars_DefaultJobItem.extend({
constructor: function(name,energy,exp)
{
energy = ((energy==0) ? 1 : energy); // prevent divide by 0
this.base(name, {'energy':energy, 'experience':exp,
'requires':arguments[2], 'drops':arguments[3]});
}
});
Creating a MafiaWars_Job object is now as simple as:
var job = new MafiaWars_Job("Corner Store Hold-up", 3, 3);
Since that particular job requires a 'Crowbar', object creation should be expanded to:
var job = new MafiaWars_Job("Corner Store Hold-up", 21, 28, {'Crowbar'});
Again, that job has other properties that need values such as the monetary reward. If not included at the time of creation, properties may be added later by calling addProperties(), as in:
Accessing the properties traces back to using the 'property' method of BaseItem, as in:
var exp2energyRatio = (job.property('experience') / job.property('energy'));
I admit there are many wrinkles to iron out with my code examples, but these foundations have proven simple to use for my current needs.
As with any language, reading it in context goes a long way to understanding it. For example, one day I ran across something that confused me (simplified to):
var e = $(id);
Searching around I found an article titled "The Javascript Dollar Sign ($) Function" which explains its use as shorthand for document.getElementById(). The article recommends not mucking with it, but I want mine to create a new div in the element the document.body:
One of the first enhancements I made was to prevent it from crashing if the element doesn't exist by creating a div element in the document.body element:
function $(id)
{
var e = document.getElementById(id);
if (! e)
{
e = document.createElement("div");
e.id = id;
document.body.appendChild(e);
}
return e;
};
Since accessing an element's value is common practice I also created:
function $$(id)
{
var e = $(id);
if (typeof arguments[1] != 'undefined')
e.value = arguments[1];
return e.value;
};
These element wrappers have proven their worth when trying to OOP HTML pages. For example, it is not uncommon to have controls such as buttons do something on the page rather than submit a form back to the server. Often this is accomplished by adding an onClick event handler to the control:
<input type="button" id="idButton1" value="Click me" onClick="DoButtonClick(this);" />
Note that 'this', 'event' and global objects such as 'document' are about the only things that the onClick, or onLoad, etc. methods can readily access, but often the client wants to include its application data to the event handler.
One method presented online is to instantiate the object before the button is clicked and include it by name in the onClick declaration, as in:
var _myApp = function () { this.doClick=function(btn){alert(btn);};};
...
<input type="button" id="idButton1" value="Click me" onClick="_myApp.doClick(this);" />
But that isn't good OOP design when the DOM model exists. Instead, when the application data is created, stash away the object in a dummy element.
function App()
{
$$('AppObjectInstance',this);
};
function DoAppButtonOnClick(btn)
{
var app = $$('AppObjectInstance');
};
...
<input type="button" id="idButton1" value="Click me" onClick="DoAppButtonOnClick(this);" />
Unfortunately the example above only allows for one object by that static name such that creating a second App() will overwrite the first in the dummy div space.
On a sidebar, a trick I learned years ago to limit an object to only a single instance is discussed by Philip Chalmers in "How To Create Unique Automatic JavaScript Objects" at:
Now just a few weeks ago someone wrote me an anonymous Facebook message asking how to use Walter Zorn's Open Source wz_jsgraphics.js library to create flowcharts at:
I jumped right on the project and within a week had something that could display the example provided me (a classic decision feedback loop) with minimal client commands. But I wanted to make it interactive, turn it into a design tool.
Luckily, Walter's package included rudimentary Image support including a string to define onClick, onLoad, onDblClick, etc. event handlers.
Using something I learned to prevent stealing popular images from one's web page, I use the graphics library to display a transparent GIF image that covers the appropriate surface area by calling the wz_jsgraphics.js library's drawImage method with something like this:
function drawButton()
{
this.graphics.drawImage(_CLEAR_GIF_URL, this.left,this.top,this.width,this.height,
'onload="DoAppItemOnLoad(this);" ondblclick="" onmouseover="" onclick=""'');
};
function DoAppButtonOnLoad(btn)
{
addClass(btn,"button");
};
The function addClass commonly found via search checks the button's list of classes and appends the new one if not found. Note that this implementation will allow multiple additions of the class type:
Adding a class style that turns the cursor into a pointer when hovering over the image gives it the appearance of being a Hyperlinked object. Also, attempts to change the cursor through DOM alone prove incompatible across browsers.
Plus, clicking on the objects is now just plain old fun... and demonstrative in my two week old example:
Please forgive that the code is in a ragtag form of transitional states.
Now before I spend all day on this note, let me just sum up by saying that ideally I think one should be able to create a web page by simply including the JavaScript web page object in the HTML header tag and creating a new myWebPageObject() in the body tag which does everything else.
Some may say Keep It Simple Stupid and stick to pure HTML whenever possible, and in many instances it makes more sense, but I believe self-contained packages are more kissable.