Languages - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Languages arrow JavaScript Objects: Part 3
JavaScript Objects: Part 3
By Jeff Cogswell

Rate This Article: Add This Article To:

In this third part of a series, Jeff Cogswell goes into more detail about the prototype mechanism that makes objects possible in JavaScript.

In my previous article about JavaScript objects, I started talking about the prototype mechanism in JavaScript. This week I go into more detail on how the prototype mechanism works.

The prototype mechanism

ADVERTISEMENT

The prototype mechanism is useful, but it might not seem totally necessary. Let's explore why it really is necessary. Take a look at this code:

function MyClass(somenum) {
    this.SomeFunction = function () {
        alert("hi");
    }
    this.SomeFunction.num = somenum;
}
obj1 = new MyClass(10);
obj2 = new MyClass(20);

obj1.SomeFunction();
alert(obj1.SomeFunction.num);

obj2.SomeFunction();
alert(obj2.SomeFunction.num);

The MyClass function serves as a constructor, and inside its code, I create a new member function object called SomeFunction. I then attach to SomeFunction a member of its own called num, storing the value passed into the constructor.

I then create two instances, obj1 and obj2, using the MyClass function. I call obj1's SomeFunction just to demonstrate the function is really there. Then I show an alert box showing the num value of obj1's SomeFunction. When this code runs, I see 10, which is what I passed in.

I then call obj2's SomeFunction, again just to show the function is there. Then I show an alert box showing that SomeFunction object's num member. And when the code runs, I see 20, which again is what I passed in.

What does that mean? The SomeFunction for obj1 has a num value of 10. The SomeFunction for obj2 has a num of 20. That means there are two distinct copies of this function. Each object has its own copy, its own complete code.

Since the code is identical, is having two separate copies really necessary? What if you're creating a thousand objects? Do you really want a thousand separate copies of the code?

Probably not. And that's where the prototype mechanism comes in. The prototype mechanism lets the objects share functions. Let's try writing a similar test using the prototype mechanism:

function MyClass() {
}
MyClass.prototype.SomeFunction = function () {
    alert("hi");
}

obj1 = new MyClass();
obj1.SomeFunction();
obj1.SomeFunction.num = 10;
obj2 = new MyClass();
obj2.SomeFunction();
obj2.SomeFunction.num = 20;

alert(obj1.SomeFunction.num);

This code creates a constructor function, and then attaches to the constructor function's prototype another function, again called SomeFunction.

I then create an object, obj1, based on the constructor. Next I call the SomeFunction member, just to prove the member is really there. And then I set the SomeFunction's num member to 10.

Next, I create another object, call its SomeFunction member, and then set its SomeFunction member's num to 20.

In the final alert box, I display the value of the first object's SomeFunction.num member. I had stored a 10 in that member, but with the second object, I stored a 20 there. Will the first object's SomeFunction.num member have a 10 or 20 in it?

If you try this code, you'll see it has a 20. That means this object:

obj1.SomeFunction.num

and this object:

obj2.SomeFunction.num

are in fact the same object. The obj1 and obj2 objects are distinct, of course. But their SomeFunction.num members are the same, which implies their SomeFunction members are the same. In other words, now we only have one copy of the SomeFunction object and one copy of its code.

Now if you make a thousand instances of the class, you'll only have a single copy of the SomeFunction method. And that's what prototypes buy you.

This, of course, is not to say you can't use the first method where the SomeFunction method is created inside the MyClass constructor code. There may well be times you want that. But you'll want to fully understand what you're doing and why that results in new function code each time the code runs.

A test that really doesn't show anything

When I wrote first sample code in the previous section, I tried to think of a way to modify the code of the function attached to the second object, to show whether the two functions are distinct. But when I thought about it, I realized what I wanted to demonstrate isn't possible.

The JavaScript object system is different from other languages, in that it's fully dynamic. When you create an object in JavaScript, the members are added dynamically. Thus, if you put code in the constructor such as this:

function MyClass(somenum) {
    this.SomeFunction = function () {
        alert("hi");
    }

then the inner function is created dynamically when the MyClass function runs, and a reference to it is saved in the SomeFunction member.

Suppose I create two instances as I did in the previous section:

obj1 = new MyClass(10);
obj2 = new MyClass(20);

Each of these instances has its own copy of the anonymous function, and each is respectively saved in the SomeFunction member variable.

So what if I then do this, shown in the final three lines of this code:

function MyClass(somenum) {
    this.SomeFunction = function () {
        alert("hi");
    }
    this.SomeFunction.num = somenum;
}
obj1 = new MyClass(10);
obj2 = new MyClass(20);

obj1.SomeFunction = function() {
    alert("good bye");
}

Will that modify the copy of the function stored in obj1? (This might seem like a trivial question, but knowing the answer is important as it shows you understand how the language dynamically adds members.)

In fact, this will not modify the function. Instead, it will create yet another dynamic function, and save a reference to it in the obj1.SomeFunction variable. So the answer is no; it won't modify the function that's already there. Instead, it'll just create a new function and change the value stored in the member variable. Now the member variable points to this brand new function.

What happened to the function that was there? It was an anonymous function, and for all intents and purposes, at this point it's gone. (JavaScript includes a garbage collector, which should remove it. Whether it really does, we can't really say without some low-level debugging tools. But we can assume it's gone, since we can't access it anymore.)

With prototypes, only functions are shared

What I just explained about prototypes applies only to functions. You are free to store member data in the prototype objects of the constructors, and each object created based on the constructor will get a copy of the data. However, unlike functions, data stored in prototypes is not shared across instances. Here's some code that demonstrates this:

function MyClass() {
}
MyClass.prototype.a = 10;

obj1 = new MyClass();
obj1.a = 50;

obj2 = new MyClass();

alert(obj1.a);
alert(obj2.a);

This is just a simple constructor, with a member variable, a, added to the prototype. I create an object, obj1, using the constructor. Then I set the object's a member to 50. Next, I create a second object, obj2. Finally, I display two alert boxes to show the values of the a members.

When I run this code, the alert messages are different. The first one (obj1.a) shows 50. The second (obj2.a), however, shows 10.

This implies two things. First, even though I changed obj1.a, the original value in the prototype is still 10. Second, even though I changed the obj1.a, the object clearly has its own distinct member. Thus, the members are distinct.

Chaining prototypes and inheritance

We're not quite done exploring the prototype mechanism. Prototypes can be chained together to create a form of inheritance. This is easier to demonstrate than explain, so here's some code that shows it in action:

function Parent() {
}
Parent.prototype.parentFunc = function() { alert ("parent"); }

function Child() {
}
Child.prototype = new Parent();
Child.prototype.childFunc = function() { alert ("child"); }

function GrandChild() {
}
GrandChild.prototype = new Child();
GrandChild.prototype.grandchildFunc = function() { alert("grandchild"); }

obj1 = new GrandChild();
obj1.grandchildFunc();
obj1.childFunc();
obj1.parentFunc();

I have three constructors here, Parent, Child, and GrandChild. The Parent constructor has a prototype containing a function called parentFunc, which simply opens an alert box showing the word parent.

But look at the Child constructor. Before storing anything in the prototype, I instead set the prototype by calling new Parent(). Remember, the prototype is itself an object accessible through the Child.prototype member. This object isn't permanent, and I'm free to replace the existing prototype with a different object. And that's exactly what I do here by calling new Parent(). This creates a prototype chain. Then I go ahead and add a function called childFunc to the prototype.

Next I do the same thing again, but with another constructor called GrandChild. This time I attach the prototype to that of Child, and then add a final function, grandchildFunc. The end result is that when I create an instance of Child, my object gets all three functions: grandchildFunc, childFunc, and parentFunc. I prove this by calling each, passing the result to alert.

If you look closely, however, you may notice a fundamental problem that should cause serious concern: In order to create the derived classes, I had to actually call the constructor function of the what will serve as a base class. Is that really a good idea? It depends on your own situation. If your constructor does something such as open up an AJAX connection and retrieves a page, you may well not want this mechanism. (However, in that case, I recommend working around the problem by putting all such code in a separate function, rather than in the constructor.)

But there might still be a problem with this. When you create a function object, the function automatically gets a default prototype object attached to it. But when you create a function object to provide an effective derived class, you do this:

Child.prototype = new Parent();

That is, you're replacing the function's default prototype object—not with another prototype object, nor with a function, but with a new object altogether that was created based on the Parent constructor!

Believe it or not, though, as odd as that may sound, that is the way JavaScript is built. If the prototype object is an object based on a constructor function, the JavaScript runtime knows to use that object's constructor function's prototype in the chaining mechanism. (Whew!)

What's next

That wraps up the introduction to the prototype mechanism. But there's still much more to objects in JavaScript! Next time I'll explore further the notion that everything is an object (including a single function itself called Function), and I'll show how interesting this can get. I'll also show how you can create an object immediately without the use of a constructor. And soon I'll show you how to mimic traditional inheritance in object-oriented programming. Stay tuned!




Discuss JavaScript Objects: Part 3
 
>>> Be the FIRST to comment on this article!
 

 
 
>>> More Languages Articles          >>> More By Jeff Cogswell
 



Microsoft's Future: A Chat With Their CTO, Barry Briggs

Play Video >

All Videos >

Julia explores the Robotics Studio!

Read now >

Messages to Bill Gates!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.