JavaScript for C# programmers: prototypes, the basics

Another post in the the series that discusses JavaScript for those more familiar with C#. In this episode, the first of a couple on the topic, we look at prototypes and prototypal inheritance.

Despite the fact that the keyword class is reserved, there are no classes in JavaScript as you'd understand them from C#. And, yet, it is an object-oriented programming language: there are objects, after all. It's all made even more confusing since JavaScript has a new keyword. How does it new up an object instance, if there are no classes?

In short, instead of using classes as a template from which you can create (instantiate) objects, in JavaScript you use other objects. These objects are known as prototypes, and JavaScript uses what's known as prototypal inheritance, rather than class inheritance. The biggest problem that people have with OOP in JavaScript is that they start off from a class-based model and try and fit that model to JavaScript. Due to the flexibility of JavaScript you can get part of the way there, but it all feels a little contrived.

So, in this series, I want you to forget all about class-based OOP and start over.

Back to basics

We've already seen how to create an object by using the object literal syntax:

var point = { x: 0, y: 0 };

However, the problem with this is that the object is a one-off. What if we needed a whole bunch of point objects? They would all look the same, that is have X and Y coordinates, and possibly have methods like move() that would translate the point in some direction on the plane, or rotate() to rotate the point a certain angle around another point, and so on. That is, we need some kind of template that defines the basic data and behavior and then use that template to stamp out point objects. In C# this is what a class does, but how's this done in JavaScript?

Let's take a look. The first thing we need to do is to write a special function known as a constructor. This will construct (that is initialize) a new object.

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

But where is the this variable coming from? In fact, what does this mean anyway?

Digression on this

In C#, this is easy: it's a reference to the current object. Since the object is an instance of a class and the method is a member of that class, when the method is called we can say with certainty which object this refers to, and what members it has and so on so forth.

With JavaScript it's not so easy. Or rather it's just as easy: this is a reference to the object the function was called on.

Here's a point object with a move method.

var point = {
    x: 0,
    y: 0,
    move: function(x, y) {
        this.x += x;
        this.y += y;
    }
};

As you can see, it's declared using an object literal. There's two fields, x and y, and the move method translates the coordinates by the displacement values passed in. It works as you'd expect from a C# viewpoint: because move is declared within point, it must always refer to that object.

point.move(1, 2);
console.log("Point = (" + point.x + "," + point.y + ")");

And the output is Point = (1,2). So here the this variable is identical to the point variable.

Now check this out:

var point3d = { x: 0, y: 0, z: 0 };
point3d.move = point.move;

point3d.move(1, 2);
console.log("Point3D = (" + point3d.x + "," + point3d.y + "," + point3d.z + ")");

Here we're defining a brand new object called point3d, with three coordinates. We set its move method equal to point's move method. (Remember, functions are objects, they are not glued forevermore to a particular definition like they are with classes in C#.) We then call point3d.move(). I think you will have worked out by now that this call will not have changed point but instead will have acted on point3d. Since the function move will have been called on point3d, the this variable will be referring to point3d. The output is Point3d = (1,2,0).

Now you're comfortable with that, check out this next bit of code.

var spacetime = { x: 0, y: 0, z: 0, t: 0 };

point.move.call(spacetime, 1, 2);
console.log("spacetime = (" + spacetime.x + "," + spacetime.y + "," + spacetime.z + "," + spacetime.t + ")");

This is downright weird, so let us take it step by step. We define a new object called spacetime. We don't define a method on it called move. Instead we call the point.move method on it and pass in the usual parameters. The call method is a method that's defined on all function objects (yes, since functions are objects they can have properties and methods on them just like any other object — this is going to be important in a moment), that takes an extra initial parameter in addition to the function's usual parameters. This initial parameter is an object on which the function gets called. This object therefore become the this variable inside the function for that call.

So the output is spacetime = (1,2,0,0).

Now, you've reread that a couple of times, all you need to remember that this refers to the object the function was called on. In particular, if you have a function inside another function (and you're already aware of this is how scope works), then the nested function's this may not be the same as the outer function's this. If you like, the scope for this is the function itself and that's it. this is always a local variable. There's no following the scope chain to try and resolve it.

By the way, if you can't "see" the object at the function's call site, it's going to be the global object, window.

Back to constructors

Looking back at our Point constructor, you can see that it refers to this. What is the value of this here? Well, if the function is called in a normal fashion:

var point = Point(1, 2);

we are going to be clobbering the global object. Why? Firstly, the object that the Point function is called on is not specified, so it is taken to be the global object, window. So, inside this call to Point, this refers to window. We'll add two new fields, x and y, and set them, and then we'll return this (which is window, remember). We then set point to this return value. Although point has the required coordinates and so looks like a point object, it's actually window, and window will have been altered. Nasty.

Instead we want to call Point, not as a normal function, but as a constructor. Enter the new keyword.

var point = new Point(1, 2);

This does what you'd expect: a new empty object is created by JavaScript and then Point is called on it. The this variable now refers to the new empty object and the statements in the function will create the new coordinate fields.

Constructors are ordinary functions

The above argument should have drilled something into you: constructors are ordinary functions, but it's the way they're called with the new keyword that makes them special. This is insane, to put it mildly. If you miss off the new keyword, you're going to be clobbering the global object and you may not even notice straight away. That's why there's a convention in JavaScript to name constructors with an initial capital letter: it's a hint to the reader that this particular function must only be called with new. Unfortunately, there's no keyword to mark a function as a constructor, nor is there some compiler/interpreter option to flag misuses for us.

Even better, there's no need to have the return statement in a constructor as I have in the Point constructor above. If there is no such statement, JavaScript will return this for you. This is yet another difference between constructors and functions:  if you don't return anything from an ordinary function, undefined is returned for you.

Inheritance

If we wanted to add the move method to our constructor, we may be tempted to do this:

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

This would work — we'd get point objects with a move method — but it contains an inefficiency. We are in effect declaring a move method for every single point object we create with the constructor, which is enormously wasteful of memory (remember the function is not compiled but interpreted every time it is called, so we are in effect copying the same source code over and over again).

We'd prefer to have the method shared, that is, declared on the template for the point objects. So far, we don't have a template, per se, just a special function that knows how to construct point objects. Enter the prototype object.

The prototype object is the template we need. It is shared amongst all objects created from it, so if there is a method declared on the prototype object it will be visible to all such objects. But where is this prototype object declared, and how can we access it?

Every function object has a property called prototype. Normally it's an empty object and is usually ignored, but it gains a special significance for constructors. A constructor's prototype is the template from which the new keyword creates the new object before calling the constructor. In my discussion above, I glossed over this and said that the new keyword would create a new empty object. Not quite; instead it creates a new object that looks like the prototype.

Let's investigate.

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;
};

What we have here is our original Point constructor definition, and then a statement that creates a property called move on the Point.prototype object. This method does the usual translation thing. We didn't need to declare prototype, since JavaScript creates one automatically for every single function. Now let's call it:

var point = new Point(1, 2);
console.log("Point = (" + point.x + "," + point.y + ")");
point.move(3, 4);
console.log("Point = (" + point.x + "," + point.y + ")");

Here we're creating a new point object and logging its value. We then call the move method on point. Unfortunately, point does not have a move method. On discovering that, JavaScript will then check the prototype for the method.

How does it find the prototype? Luckily for us, the new keyword has set up a hidden property for us in the new object called constructor that references the constructor function that was used to create the object; in our case, Point. So, a new object gets an extra property called constructor that points to the constructor function, and as we've seen the constructor function has a property called prototype.

So, if the method is not found in the object, JavaScript will go take a look at the prototype object to see it it has the method, instead. If so, it calls it. If it hasn't, JavaScript will check to see if the prototype object was constructed and go find it's prototype object to see if it has such a method. And so on, up the prototype chain.

Yes, you've guessed it: this is JavaScript's inheritance. Constructed objects inherit their behavior from their prototypes. Even better, given the description of how a method is called on an object, we can see that if I declare a move method on a newly created point object, it will get called in preference to the prototype object's. In fact, it hides the prototype's version. That's usually known as polymorphism and we're overriding a method.

Welcome to prototypal inheritance and polymorphism, all without a class in sight.

Oh, by the way, remember the call method above that we used to set an explicit this variable on calling a function? It's a method that's defined on all functions; or, translating into what we now know, it's a method that's defined on a function's prototype. The constructor for a function object is Function.

We'll continue with this topic next time.

Album cover for Living in Fear Now playing:
Power Station - Taxman
(from Living in Fear)



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

2 Responses

#1 Dew Drop - February 24, 2009 | Alvin Ashcraft's Morning Dew said...
24-Feb-09 6:22 AM

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

#2 JavaScript for C# programmers: prototypes and privacy said...
27-Feb-09 10:17 PM

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

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