You may have heard that Javascript is a prototype-based programming language, but chances are that you've never used it that way -- not really. Maybe that's because prototype-based programming is kind of... well... different.
Now, I must admit that I think prototype-based programming makes sense to me, but I haven't ever actually used it in a project. (Until now.) In fact, you might be hard pressed to find any projects in which this programming model is being used, and I am curious to know why. Is it because it is awkward, or is it a hidden gem?
My interest is both academic and practical, in the sense that I love to explore programming language methodologies but I also actually write code. It seems, other than Javascript, the prototype model exists largely in academia. You'll hear a lot about Self, a Smalltalk derivative, but I am unaware of any projects actually written in Self. My favorite example of a prototype-based programming language is Io -- in which I have never written a single line of code. But its documentation helps to illustrate the point. I will translate one such example into how Javascript would look if it were more straightforwardly prototypal (and then we'll work on making it so).
Account = Object.clone();
Account.balance = 0;
Account.deposit = function(amount) {
this.balance += amount;
}
account = Account.clone();
account.deposit(10.00);
alert(account.balance);
In the above example, notice that there are no classes nor constructors. Account is an object. It has a balance. You can even call the deposit method to change that balance. But the Account object was not likely created to be used directly. Instead it is intended to be more of a rubber stamp, a model which can be cloned.
And under the hood, Javascript works this way too. The confusion comes largely from the adhoc type system that is built on top of the prototype model. Take away the new and instanceof operators and using prototype inheritance in Javascript might be more straightforward. (I am not suggesting this, however. I love the flexibility that Javascript provides.)
The previous Javascript example does not work, of course. There is no Object.clone() method. But that is essentially what the new operator does. (More on that later.) In SpiderMonkey Javascript (Mozilla's implementation), the above code could be rewritten as:
Account = {
balance: 0,
deposit: function(amount) {
this.balance += amount;
}
}
Account.__proto__ = Object.prototype; // Object itself is a constructor,
// not an 'instance'
account = {};
account.__proto__ = Account;
account.deposit(10.00);
alert(account.balance);
And this more clearly shows what is actually going on and the relationship between objects in prototype-based models. Account defines two properties, balance and deposit, but it also declares that it inherits properties from Object.prototype by setting Account.__proto__ = Object.prototype. Thus if you attempt to access a property that does not exist for Account, Object.prototype will also be searched. However, it is important to remember that each object has its own properties (or slots). In the above example, setting account.balance = 1000 does not change Account.balance. This is a good thing.
It is worth mentioning also that in some major Javascript implementations (most notably IE), you cannot set an object's prototype chain directly as we did in the prior example. It must be set on object "construction".
Constructors are the adhoc type system of which I spoke earlier. They are nothing more than simple initializer functions, but they also are responsible for setting the prototype chain by way of their prototype property. As always, code speaks louder than words. Below is what the new operator might look like in SpiderMonkey.
function neww (constructor) {
var newObject = {}; // new object with its own properties
newObject.constructor = constructor; // this is the new object 'type'
newObject.__proto__ = constructor.prototype; // desired prototype obtained here
// call the constructor on the newly created object
constructor.apply(newObject, Array.prototype.slice.call(arguments, 1));
return newObject;
}
function FauxAccountClass() {
}
FauxAccountClass.prototype = Account;
account = neww(FauxAccountClass);
// or, same thing...
account = new FauxAccountClass();
alert(account instanceof FauxAccountClass); // will alert 'true'
Please let me know if something is not clear. To summarize, a constructor is simply a function. The prototype property of that function (eg. FauxAccountClass.prototype) decides what the "parent" object of the new object will be. Constructors are the basis of the Javascript type system. However, if you "forget" about constructors and the operators designed to work with them, you still have Javascript ...and an entirely different way of using it. That's where I'm going to take you next. But this article is already quite long, so I'll continue in a follow-up post.
The follow up is now online here: http://devblog.techhead.biz/2009/04/prototype-based-programming-in_04.html
0 comments :: Prototype-based programming in Javascript
Post a Comment