JavaScript for C# programmers: prototypes and privacy

Continuing my series about learning JavaScript when you're a C# programmer, using Firebug in Firebox as our testing ground.

In this episode, overriding, privacy, and class models.

Last time we saw how to create inheritance from JavaScript's constructors and prototypes, the so-called prototypal inheritance. In our example, we ended up with this:

var Point = function(x, y) {
    this.x = x;
    this.y = y;
    return this;
};
Point.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
};

This code snippet shows a couple of things I'd like to reinforce. First of all, because move is defined on the prototype for Point, it is visible to all objects we create from that constructor. If you like, since the prototype is the template for Point objects, all Point objects will have a move function. Second, the x and y fields are not shared between Point objects, but nevertheless all Point objects will have their own copies of these fields.

The concept of a class as we know it in C# is essentially split between the constructor and the prototype. If some variable (including a function) for an object is defined in the constructor, all objects created from it will have their own copy of that variable. If some variable for an object is declared on the prototype, all objects created using it will share the one copy from the prototype.

Overriding fields

But, note, however, there is a gotcha with that last sentence: it is only true when you read from the object. Let's investigate using my overworked Point example. First I'll declare a new field for the prototype called color, and I'll set it to "Red":

Point.prototype.color = "Red";

Now let's create a couple of points:

var first = new Point(1, 3);
var second = new Point(4, 2);
console.log(first.color);  // outputs Red
console.log(second.color); // outputs Red

When I read the value of first.color, JavaScript will first go to the object and see if it has a property called color. It does not. The interpreter then goes to the object's prototype object to see if it has a property called color (remember how it does this: the object first has a hidden field called constructor that points to Point, and this has the field called prototype, which is the prototype of the original object). It does, so the value of color, "Red", is returned. The same exact process happens when I call second.color. All is good; it makes sense.

What happens if I now set first.color to "Blue". What does second.color return now?

first.color = "Blue";
console.log(second.color);

There's two possible answers, "Red" or "Blue", and it hinges on what happens when first.color is set. Pat yourself on the back if you said "Red" and here's why. The chaining to the prototype only happens on a read operation. If you are writing a value, you will be modifying the object itself. So, since there is no property called color in the first object, JavaScript will create one and set it to "Blue". The common prototype.color property is not changed at all. Hence, when you read second.color, you get the chaining to the prototype operation, and "Red" gets returned.

You are, in effect, overriding a field from the object's prototype. The same thing happens when you set a function with the same name as a function in the prototype: you will override the prototype's function.

Constructing objects with private fields

In playing around with this Point example over the past few articles, I've gone from an object that had private fields but that didn't use a constructor/prototype, to an object that's lost its privacy but does have this notion of classical inheritance. Can we get the privacy back?

Remember that privacy comes from closure. Which is the only function we have that can supply privacy? The constructor. Here's a version that implements x and y as private variables:

var Point = function(x, y) {
    this.getX = function() { return x; };
    this.getY = function() { return y; };
    this.setX = function(value) { x = value; };
    this.setY = function(value) { y = value; };
    return this;
};
Point.prototype.move = function(x, y) {
    this.setx(this.getX() + x);
    this.sety(this.getY() + y);
};

Oh wow, it's suddenly a lot more complicated. The first thing to realize is that x and y are parameters to the constructor function, so they are automatically local variables and therefore private. Even more restrictive, since they are local to the constructor, they can't be seen outside the constructor, in particular by the common properties and methods of the prototype. So, the prototype's move method can't reference x and y (at least not without getting an undefined error). We have to therefore write some functions that are public and that can reference the private variables. Those functions by necessity must be defined in the constructor. And so I defined a set of getters and setters.

Note that these getter and setter functions are defined in each object, and not on the prototype. This means we're duplicating the code, but there's no way round it. It also means they can't participate in inheritance: they're defined on individual objects.

Notice also that the getters and setters are public. We are declaring them on the newly created object, and so the move method can make use of them. Douglas Crockford (the inventor of private properties in JavaScript) calls these kinds of functions privileged. A privileged function is a public method on an object that can get at the private data of the object.

Defining descendants

We've now gone into some depth about how to "do" classes by defining a constructor and by defining properties and methods on the constructor's prototype. We can define, in essence, a template for stamping out a whole set of similar objects and we know how to override properties and methods in our newly created objects. But what about further inheritance, building up a class model?

Suppose, now that we have a Point "class", we need a descendant, a ColoredPoint class, which knows its color. How is that done? We obviously need a new constructor, but that doesn't define the inheritance pattern, it's the prototype of that constructor that does. (We'll go back here to our non-privatized version of the Point "class" to avoid the noisy getters and setters.)

var ColoredPoint = function(x, y, color) {
    this.x = x;
    this.y = y;
    this.color = color;
    return this;
};
ColoredPoint.prototype = new Point(0, 0);

Notice that I've thrown away the automatic prototype object that's created with the constructor and replaced it with a fresh new Point object. I'm not particularly bothered about what values I pass when constructing this Point object, I won't be using them.

var coloredPoint = new ColoredPoint(1, 3, "Red");
coloredPoint.move(2, 2);

console.log("Point = (" +
        coloredPoint.x + "," +
        coloredPoint.y + "," +
        coloredPoint.color + ")");

Here I'm constructing a new ColoredPoint object and then I call move on it. What happens here? First, the coloredPoint object has no method called move. So JavaScript goes to the prototype object. The prototype object doesn't have a move method either. So JavaScript goes to its prototype, finally, which does have a move method. Notice how the process continues down the prototype chain, and that at the end, the function is called on the original object (which defines the value of this). The output therefore is Point = (3,5,Red) which is what we wanted and expected.

Next time, we'll throw all that away. We don't need this lookalike classical class model. Let's embrace objects!

Album cover for Zenyatta Mondatta Now playing:
Police - Voices Inside My Head
(from Zenyatta Mondatta)


Loading similar posts...   Loading links to posts on similar topics...

1 Response

#1 Dew Drop - February 28, 2009 | Alvin Ashcraft's Morning Dew said...
28-Feb-09 6:21 PM

Pingback from Dew Drop - February 28, 2009 | Alvin Ashcraft's Morning Dew

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response