JavaScript is described as a prototypal language because of it’s use of a shared prototype/cookie cutter for objects. It’s probably easier to understand with some examples, though.
function User (name) {
this.name = name;
}
var jon = new User("Jon");
alert(jon.name);
The above will show an alert dialog with the name of the user, “Jon”. All instances of User will have a name property, but we can can do more:
function User (name) {
this.name = name;
this.say_name = function() {
alert(this.name);
}
}
var jon = new User("Jon");
jon.say_name();
Now all users will have a method, say_name. Unfortunately, every User instance has a new method called say_name. It's not a complex function, but there's potentially a lot of needless duplication. If say_name doesn't need to know private details, we can do the following instead:
function User (name) {
this.name = name;
}
User.prototype.say_name = function() {
alert(this.name);
}
var jon = new User("Jon");
jon.say_name();
Now all User instances share the same say_name function.
A couple of days ago, I posted about First-class functions in JavaScript and in particular the map function. As written, it works fine, but how could we take advantage of the prototype property shown above. I mentioned that JavaScript 1.6 adds support for a map function, but older browsers don't support it so we can do this instead:
if (typeof Array.prototype.map == "undefined") //check if it isn't already defined (e.g. IE 6)
{
Array.prototype.map = function (callback) {
var ret_array = [];
for(var index = 0, length = this.length; index < length; ++index) {
ret_array[index] = callback(this[index]);
}
return ret_array;
}
}
Now there is one small problem. The map function defined in JavaScript 1.6 takes an optional 2nd parameter for the context of this. We can fix the above by changing the method as follows:
Array.prototype.map = function (callback, context) {
var ret_array = [];
for(var index = 0, length = this.length; index < length; ++index) {
ret_array[index] = callback.call(context, this[index]);
}
return ret_array;
}
I've used the call method which belongs to all Function objects. See call at MDC for more detail on that. It allows you to change the context of the execution of a function... letting 'this' refer to something different. I'll write more about that later as it really deserves its own post.
So, now we have a standard compliant, backwards-compatible (back to JavaScript 1.3 compliant browsers) map function. If the browser supports it, the native map function will be used, and if not, our substitute will be used instead. Feel the Win.
There's a lot more you can do with this, but that should be enough to get started.