by Rob Locher
I had a job working for a government contractor once, and we were writing a web application for a large governmental organization that had a peculiar requirement: they didn't want any programs installed on their servers. Apparently classic ASP didn't count as a “program”, but ASP.NET did. Go figure. So the boss told us to use good old classic ASP. Fortunately, nobody said we had to use VBScript (ugh) on the server side; we were allowed to use JavaScript, a vastly more powerful and useful language. By the way, JavaScript has been called “The World's Most Misunderstood Programming Language”, and I agree with that assessment: it's far more powerful and useful than people give it credit for.
So my lead developer on the project, Bernie, responded to the constraints by sketching out a JavaScript object framework that we called “Poor Man's Dot Net”, or PMDN for short. This framework leveraged JavaScript inheritance to a degree I've never seen elsewhere. It was some tremendously clever code. Unfortunately the framework was difficult to explain and document because of its dependence on obscure features of an underestimated language mainly used for the humble task of scripting web pages. As far as I know PMDN was never used for any other project.
The purpose of this document is to list JavaScript features related to inheritance that are hard to find in books and web articles, without presenting any nonsense about how JavaScript is truly an object-oriented language. (I trust you to make up your own mind whether JavaScript is OO or not.) I apologize in advance for the boring examples, but they do neatly present all the concepts I try to get across; the best way to read them is to try to predict what the alert dialogs will show before you click the buttons.
To be fair to you the reader, I must tell you about some better-written articles about JavaScript by Douglas Crockford, available on his web site. I was half finished writing this article when I came across his articles, which completed my understanding of the concepts I am trying to explain. Some of the things he explains, and I mean important concepts and not just obscure details, are not documented anywhere else.
I was just as surprised as anyone to discover that JavaScript does in fact have private member variables and methods. There is a catch, though; some public methods can't use private methods. However, like so many things in JavaScript, there is a work-around: the "privileged" method and the "self" private member. Please read Douglas Crockford's article to see what I'm talking about.
In JavaScript, primitives are copied by value, and objects are copied by reference. A "primitive" is a native JavaScript type not derived from Object, such as an integer. "Copied by value" means that if you make a copy of something and then modify the copy, the original is not changed. "Copied by reference" means that if you make a copy of something and then modify the copy, the original is changed also. See the following example for a demonstration. Unfortunately, sometimes it is not at all obvious whether you are dealing with a primitive or an object. For example, var emperor1 = "Napoleon"; is not the same as var emperor1 = new String("Napoleon");. When in doubt, the best thing to do is experiment.
<script language="javascript"> function objectType(input) { this.property = input; } function CopyModify() { var v1 = "a value"; var v2 = v1; v2 = "something else"; alert(v1); var f1 = new objectType("initial value"); var f2 = f1; f2.property = "some other value"; alert(f1.property); } </script> <input type="button" value="Click here" onclick="CopyModify();">
A not-so-obvious feature of JavaScript is that functions are data. Consider the following:
<script language="javascript"> function clickHandler() { var squared = function(x) { return x*x; }; alert(squared.toString()); } </script> <input type="button" onclick="clickHandler();" value="Click here"/>
This implies that if you have a simple JavaScript object with methods, then it carries its methods with it. More importantly, if you have a thousand instances of a simple object that has one method, then the JavaScript interpreter has to maintain a thousand copies of that method in memory!
Each object (not each instance) is given a prototype, which is another object. In other words, all instances of an object share a single instance of the prototype object. If a method or member variable of an object is called or accessed, the JavaScript interpreter first checks the object to see if it has a method or member variable by that name; if not, it checks the object's prototype for the method or member variable. The problem of a thousand copies of the same method can be neatly solved by making the method be the method of the prototype of an object, in which case the thousand instances all share one copy of the method. See the following script for an example of how to put a method in the prototype rather than in the object.
<script language="javascript"> function testObj() { this.property = "property"; } testObj.prototype.method = function() { alert("method"); alert(this.property); }; // mandatory semicolon </script> <input type="button" onclick="var obj1=new testObj();obj1.method();" value="Click here"/>
There are some interesting things to note about this example:
The closest thing to inheritance that JavaScript has to offer is implemented by making the prototype of a derived object be an instance of the base object. Other books and articles cover this subject better than I have time for, but here is a (not very exciting) example:
<script language="javascript"> function bas() { this.method = function() { alert("hidden"); } } function der() { // do nothing } der.prototype = new bas(); function clickHandler() { var obj = new der(); obj.method(); obj.method = function() { alert("hides"); }; obj.method(); } </script> <input type="button" onclick="clickHandler();" value="Click here">
Note how first the prototype method is called, then a method is added to the object which hides the method in the prototype.
Bernie never bothered with prototypes. He created complex object hierarchies by having the constructor of a derived object create an instance of the base object, and then modifying that instance. Once you've seen the idea it seems obvious, but I've never seen it documented anywhere but right here. See the following example:
<script language="javascript"> function base() { this.method = function() { return "xxx"; } } function derived() { var f = new base(); f.method = function() { // New method replaces old return "yyy"; } f.additiveMethod = function() { var f2 = new base(); return f2.method() + " yyy"; } return f; } </script> <input type="button" onclick="var obj1 = new derived(); alert(obj1.method()); alert(obj1.additiveMethod());" value="Click here"/>
Notes:
Why not combine the two techniques? What I mean is that it is possible to put an object's methods in its prototype for efficiency, and still use parasitic inheritance as your inheritance mechanism.
<script language="javascript"> function CopyPrototype(from, to) { if ("object" != typeof(from) || "object" != typeof(to)) throw "CopyPrototype() can only be passed parameters that are objects."; for (var member in from) to[member] = from[member]; } function hybridbase() { this.simpleProperty = 5; } hybridbase.prototype.simpleMethod = function() { return this.simpleProperty += 1; }; function hybridderived() { hybridbase.call(this); } CopyPrototype(hybridbase.prototype, hybridderived.prototype); hybridderived.prototype.anotherMethod = function() { alert(this.simpleMethod()); }; function DoIt() { var f = new hybridderived(); f.anotherMethod(); alert(f.constructor.toString()); } </script> <input type="button" value="Click here" onclick="DoIt();">
Notes:
Assuming you are using parasitic inheritance with prototypes, here are the general patterns to follow.
Notes:
Here are some tips I discovered: