Tuesday 24 June 2008

Getting intimate with Javascript objects part #3 - prototypes and verrucaes

Gentle reader ,
have you got your geek waders on because the Javascript puddle we are about to paddle in can get quite deep? Today I am poking "Prototyping" in the eye with a stick and then running away in the hope that you can get an inkling about how Prototyping works so that properties and methods can be "inherited".

Prototyping is a prebuilt keyword that simplifies the process of adding custom properties/ methods to all instances of an object.

In JavaScript, you're allowed to add custom properties to both prebuilt and custom objects. Here's an
example of each:

//adding a custom property to a prebuilt object
var MySexyGirlfirend =new Image()
MySexyGirfriend.vitalstatistics="36:24:36"

//adding a custom property to the custom object verrucae
//First, create the custom object "verrcuae"
function verrucae() {}
var ItchyLeftFoot=new verrucae();
ItchyLeftFoot.nodules = 4;


A custom property added this way only exists for that instance of the object. If I were to spit out another instance of verrucae(), called ItchyRightFoot, for example, ItchRightCircle.nodules would by default return "undefined" until I go over the process again of first defining ItchyRightFoot.nodules, as with ItchyLeftFoot example above.

There are times when you'll want to add a custom property that's only reflected on a particular instance of an object, and times, when you just wished all instances of the object would recognize the custom property. For example, all verrucae objects have a default nodule count of 4, so it's reasonable to assume you'd like the custom property "nodules" to added to the verrucae in a way so that it's default across all instances of the verrucae object that you create. That's where the prototype methodology comes in.

The prototype methodology is here to help when you wish to quickly add a custom property or method to an object that will be reflected on all instances of it. To use it simply reference the keyword "prototype" on the object before adding the custom property to it, and this property is instantly attached to all instances of the object.
Consider this :

function verrucae() {}
verrucae.prototype.nodules=4;
var ItchyLeftFoot = new verrucae();
var ItchyRightFoot = new verrucae();

On the second line I added a prototype property to the object. So ItchyLeftFoot and ItchyRightFoot both have a property of .nodules = 4 without having to manually set it.

** Updated **
There is an important thing to take note at this point in our wander. While you are free to use the prototype object on any custom objects, this is NOT the case with some of the prebuilt objects. Whilst those lovely people at Mozilla allow you to prototype most if not all objects IE allows you to "prototype" those objects that can be created with the "new" keyword, such as Image(), String(), Date() ,Object() , Array(). So if your IE users start complaining that they are getting "Object does not support this method" error messages and your Firefox users are OK .. you will know what the problem is.

.o0(yes indeed I do know what the problem is.. IE is a heap of POO!.. short simple and elegant summation I think)

The prototype object can also help you quickly add a custom method to an object that is reflected on all instances of it. To do so, simply create the object method as usual, but when attaching it to the object (you guessed it), use "prototype" beforehand. Let's extend our verrucae object so it contains a default method that does something simple, like alert the discomfort of the owner.

//First, create the custom object
function verrucae(){}
verrucae.prototype.nodule=4;
//Then create the function you want to attach to the object
function v_ouch ({ alert('OUCH my verrucae has '+this.nodules+" nodules and it is very sore!')}
verrucae.prototype.ouch= v_ouch


Now, all instances of the verrucae object contain a ouch() method!

As mentioned before, the prototype methodology can be used on pre-built JavaScript objects but ONLY those created with the "new " keyword. Let's see an interesting example on how to extend the prebuilt String() object of JavaScript to include a method that writes any string as pig latin when called upon:

function v_pigMe()
{

var word = this.split(" ")
outp = ""
for (i = 0; i < word.length; i++)
{
thisword = word [i]
alert(thisword)
outp = outp + thisword.substring (1, thisword.length) + thisword.substring(0,1) + "ay "
}
alert(outp)
}
//Attach custom method to string object
String.prototype.pigMe=v_pigMe;

The above code may not look like much, but it just added a whole new functionality to the default string object - the ability to output any text in pig latin. Surely a bonus in this modern multicultural world?

** Comming Soon ** Fooling JS into the notion that it has classes.

6 comments:

Tim Tripcony said...

Steve, this has been a great series so far... JavaScript is a very misunderstood language, and the upcoming changes in 8.5 will make a deep understanding of the language even more important to our community.

Couple quick comments:
- The generally accepted syntax to empty object and array instantiation is now simply {} and [], respectively... no need to specify new Object() or new Array().

- You mentioned that the prototype property cannot be modified on core objects, but (unless I'm misunderstanding what you mean) that's not entirely accurate. For example, you could do the following:

Array.prototype.customArrayMethod = function () { alert ('Every array now has this method'); };

...it's just not recommended, as mucking about with the core objects can have some serious unintended consequences if you're not careful. In fact, the now ubiquitous Prototype framework originally took its name from its tendency to add all manner of custom methods to Object.prototype, Array.prototype, and the like. They've since scaled back a bit on that because the scope of the modifications they were making to core objects made a lot of current and potential implementors of the framework nervous.

Steve McDonagh said...

Tim-
Thanks :-)

On the NEW front ,indeed so, LOL but old habits die hard. But the proscription on using prototype does (or did perhaps it has changed??) does affect any of inbuilt objects that are not instantiated with a NEW . The rule I have stuck by is "no constructor no prototype" this generally keeps me out of trouble when coding for IE [spits on floor] Mozilla exposes most if not all of the constructors so you can use prototype there. But trying doing document.prototype.getElementsByClassName = function(para)
{alert(para))
in IE .. works in Mozzy browsers but not in IE.

Perhaps I should have been clearer about that :-)

Steve McDonagh said...

@Tim

I updated one of the para's in the article to be slightly more readable in the light of your comments. Or at least I hope it makes it more understandable.

Steve

Steve McDonagh said...

@Tim
sorry just read my comment above "...does affect any of inbuilt objects that are not instantiated with a NEW" should read
does affect any of inbuilt objects that can not be instantiated with a NEW"

Tim Tripcony said...

@Steve, that's exactly right.

This works:

var myObject = {};
var myArray = [1,2,3,4];
var myString = 'test';
Object.prototype.speak = function () { alert('Hello'); };
Array.prototype.listen = function () { prompt('Hello?'); };
myObject.speak();
myArray.listen();
myString.speak();

Because modifications to an object's prototype (including the Object object) affect all instances, each object we created has been given a new method, even though the objects were created before the methods were defined, and both objects were created without new... in fact, myArray and document now include speak() as well because everything's an object and we modified all objects.

This does not work:

document.prototype.speak = function () { alert('Hello'); };
document.speak();

document has no prototype property because, as you pointed out, you'll never create a new document... you only get the one the browser gives you.

But this does:

document.speak = function () { alert('Hello'); };
document.speak();

Although document has no prototype, you can still modify it directly... you just can't modify its prototype because it doesn't have one.

In other words, the assertion that the prototype property is missing from any object that can never be copied using the new keyword is essentially accurate; I'm just clarifying that arrays, strings and other objects can be created without the new keyword and still be impacted by changes to their prototype chain.

Steve McDonagh said...

Elegantly put Tim :-)
I am still relatively new to this business of getting what I know down in a way that people can understand.
But what I lack in style of composition I more than make up for with weird metaphor ;-) LOL

Disqus for Domi-No-Yes-Maybe