I was teaching my friend Joe about how prototype works in JavaScript; and
how you can use it, essentially, to create static properties across multiple instances
of classes.
For those that haven't used prototype in JavaScript, the concept is fairly straight-forward.
You create a class by defining a function and afterward set new properties and methods that will
be "shared" by multiple instances using {className}.prototype.{propertyOrFunctionName} and assigning
them properly:
function MyClass( someString )
{
// Every object instance created from MyClass will have its own InstanceProperty
// and it will be assigned whatever the "creator" passes into the constructor as its value.
this.InstanceProperty = someString;
}
MyClass.prototype.StaticProperty = "Hello World";
//=================================
var instance1 = new MyClass("Instance 1");
var instance2 = new MyClass("Instance 2");
Now we have:
instance1.InstanceProperty == "Instance 1";
instance2.InstanceProperty == "Instance 2";
instance1.StaticProperty == "Hello World";
instance2.StaticProperty == "Hello World";
Let's take instance2 for example. When we access the StaticProperty, JavaScript looks at the
instance and asks, "Do you have an 'instance property' called "StaticProperty?"
Instance2 responds with a "no", so JavaScript decides to examine the MyClass.prototype and see if
it's defined there. It sees that it is and returns the string "Hello World".
Those that haven't used this before are saying to themselves, "... And I care because ...?!?!?!"
Think about this set of code:
function MyClass()
{
this.InstanceProperty = "Hello World";
this.ShowInstanceProperty = function(){ alert(this.InstanceProperty(); }
this.ReallyLongFunction()
{
var result = "";
for( var x = 0; x < 1000; x++ )
{
result += x + "<br />\n";
}
//... does a lot more ...
}
this.SomeOtherFunction()
{
// ... does even more ...
}
}
var instance1 = new MyClass();
var instance2 = new MyClass();
var instance3 = new MyClass();
var instance4 = new MyClass();
var instance5 = new MyClass();
Each time the MyClass constructor above is called (in this case 5 times...) we end up creating new
copies of all instance members in new memory space. That means the
ShowInstanceProperty, ReallyLongFunction, SomeOtherFunction functions get 5 copies made; making 15
functions in memory space that do the same things. What a waste of memory!
Using prototype instead causes the same 3 functions to only be created once in memory space; only one
of each. Look at the "prototyped" code:
function MyClass()
{
this.InstanceProperty = "Hello World";
}
MyClass.prototype.ShowInstanceProperty = function(){ alert(this.InstanceProperty(); }
MyClass.prototype.ReallyLongFunction = function(){
var result = "";
for( var x = 0; x < 1000; x++ )
{
result += x + "<br />\n";
}
//... does a lot more ...
}
MyClass.prototype.SomeOtherFunction = function()
{
// ... does even more ...
}
var instance1 = new MyClass();
var instance2 = new MyClass();
var instance3 = new MyClass();
var instance4 = new MyClass();
var instance5 = new MyClass();
It looks a little odd at first; but once you get used to it, it's "No Big." And trust me when I say, it's
the better way to go. Think about if MyClass had 30 functions defined for it that all had 10 lines of code
each. Now pretend we went with the "instance functions" example and through our script ended up creating 1000
instances of MyClass. That amounts to 30 functions times 10 lines of code times 1000 instances.
30 x 10 x 1000 = 300,000 lines of code eating up memory space now.
If we use prototype: 30 x 10 x 1 = 300 lines of code taking up memory space.
Nevertheless, I digress. The story for today wasn't supposed to focus on that. That really needs to go into
its own document on this site; which I hope to get to at a later time (like the rest of the projects I've
uploaded to the site but haven't had time to document, so they're not listed... one day!)
Back to the story for today...
So I was showing Joe some code for demonstrating how prototype seemed to make the concept of Static Properties
availble across multiple instances of objects. Then it dawned on me that I'd never tried assigning the
"Static Property" after creating instances of a class that has been given prototype properties.
Can you guess what happened? Here's some sample code:
function MyClass( someString )
{
this.InstanceString = someString;
}
MyClass.prototype.StaticString = "Hello World";
//=====================
var instance1 = new MyClass("Instance 1");
var instance2 = new MyClass("Instance 2");
alert(instance1.StaticString); // output: "Hello World";
alert(instance2.StaticString); // output: "Hello World";
// Attempt to assign the static property, with the intent of updating what both instances see.
instance2.StaticString = "Some New String";
alert(instance1.StaticString); // output: "Hello World";
alert(instance2.StaticString); // output: "Some New String";
What the...?! After thinking about it for a second, it made total sense to me; and this is something important
we should all remember when programming in JavaScript...
Writing a Property
When you access a property to set its value and that property doesn't exist on the object yet,
JavaScript will create the instance property without looking at the prototype and make the assignment.
Reading a Property
When you access a property to get its value, it will look at the instance first and see if it's got an
instance property with that name. If it doesn't, it will check the prototype and return its value
(if it exists there.) If it doesn't exist on the prototype, JavaScript will throw the infamous
"Object doesn't support this property or method." error.
So, it was the best of times, it was the worst of times... what was two instances talking to one
"static property", we now had one talking to a "static" and one talking to an "instance".
This got me thinking even harder about it and wondering if there was some way to still use it as a static
property.
Sure... since JavaScript doesn't put constraints on when you can/can't set the value of a property, you can
reset the value of the prototype property by setting it the same as when you initially created it... Here's
our example again showing this concept:
function MyClass( someString )
{
this.InstanceString = someString;
}
MyClass.prototype.StaticString = "Hello World";
//=====================
var instance1 = new MyClass("Instance 1");
var instance2 = new MyClass("Instance 2");
alert(instance1.StaticString); // output: "Hello World";
alert(instance2.StaticString); // output: "Hello World";
// Re-assign the static property, with the intent of updating what both instances see.
MyClass.prototype.StaticString = "Some New String";
alert(instance1.StaticString); // output: "Some New String";
alert(instance2.StaticString); // output: "Some New String";
This is really great for scenarios where someone using your class might be creating multiple instances
of your class; but you want to ensure that the functionality from each instance always talks to a "central"
property for data manipulation. Later on if/when I get more time I'll update the site witha fun
proof-of-concept I worked on tonight for handling multiple window.onload events to ensure when
multiple scripts are included in a page and could potentially all be assigning window.onload.
For now, my sleep-deprived body won't let me do anymore. Until next time... I hope this will help in our quest
to become JavaScriptorians - Head of the "Class". :)
Happy Scripting...