Adventures in JavaScript: Objects and Prototypes

green-lanternLast time, I got started with JavaScript by doing the Roman Numerals kata.

I got the kata to work, but like all first steps, it felt awkward. The main reason is that JavaScript has a different object model than I’m used to.

Let’s suit up and shine some light on that model.

Objects

Things in JavaScript are either primitives or objects.

Objects can be created using literals:

var romanNumeral = {
  symbol: "i",
  value: 1
};

A new object can also be created by the new operator and a constructor. The constructor can refer to the newly created object with this:

function RomanNumeral(symbol, value) {
  this.symbol = symbol;
  this.value = value;
}

thingIn JavaScript, an object represents a table relating names to values.

The constructor above relates the name string to the object provided in the name parameter. (Let’s hope that object is actually a string.)

Name and value together are referred to as a property.

Values are things again, so either primitives or objects. Functions are objects too, as we’ll see below.

Here’s how someone with a Java background like me might initially try to code a JavaBean-like object:

function RomanNumeral(symbol, value) {
  this.symbol = symbol;
  this.value = value;

  this.getSymbol = function() {
    return this.symbol;
  };
  this.getValue = function() {
    return this.value;
  };
}

There are some problems with this piece of code, however.

Methods

daredevilThe first issue with the JavaBean-like code is that it’s built on the mistaken assumption that the symbol and value properties are private.

The properties of a JavaScript object are automatically exposed. Nobody is blind to your internals in JavaScript!

Luckily, JavaScript does provide a reliable mechanism for information hiding, namely the closure:

function RomanNumeral(symbol, value) {
  this.symbol = function() {
    return this.symbol;
  };
  this.value = function() {
    return value;
  };
}

Here the value of the symbol property is a function rather than a string. Functions in JavaScript are first-class citizens and can be passed around like any other object and then be called later.

Functions can refer to any variable in their scope, including the parameters and variables of outer functions.

So the closure assigned to the symbol property can refer to the symbol parameter provided to the constructor even when that parameter is out of scope at the place the closure is actually called!

Class Methods vs Instance Methods

The second problem with the initial code, and also with the improved code above, is that it creates new function objects and assigns them to the object’s properties every time an instance is created.

In the closure case, that is actually what we want, since the closure should have the constructor’s parameters in its scope for it to work properly.

In the original code, however, we end up with too many function objects. There will be one getSymbol function object per instance, for example. We can reduce that overhead by defining the function on the prototype:

function RomanNumeral(symbol, value) {
  this.symbol = symbol;
  this.value = value;
}

RomanNumeral.prototype.getSymbol = function() {
  return this.symbol;
};
RomanNumeral.prototype.getValue = function() {
  return this.value;
};

prototypeEvery object is associated with a prototype object. The prototype property is set automatically by the constructor.

With the above code, all objects created with new RomanNumeral(...) still have their own symbol property.

But now they all share the same instance of the getSymbol() function, because they access it through the prototype property that points to a separate object.

We can use the same trick with non-function properties too:

function RomanNumerals() {
  // ...
}

RomanNumerals.prototype.ROMAN_NUMERALS = [
  // ... other numerals ...
  new RomanNumeral("iv", "4"),
  new RomanNumeral("i", "1")
];

This is analogous to static variables in Java.

Subclasses

Let’s leave the Roman numerals behind and move into more interesting territory. Superheros have the ability to display their superpowers:

function SuperHero(name) {
  this.name = name;
}

SuperHero.prototype.showPowers = function() {
  beAwesome();
};

Some superheros can fly and therefore have an altitude:

function FlyingSuperHero(name) {
  SuperHero.call(name);
  this.altitude = 0;
}

FlyingSuperHero.prototype = Object.create(
    SuperHero.prototype);

FlyingSuperHero.prototype.flyTo = function(altitude) {
  this.altitude = altitude;
};

avengersHere we see some very powerful things at work.

First, a function is an object and can therefore have properties. The call() method is one such property.

Second, prototype is a property too, and can be set! We use this to create a new object with its prototype set to the object that represents the base class’ prototype.

Note that since objects are basically hash tables, we can’t simply override showPowers and call the super class’ version. There are some ways to achieve that, but they don’t look pretty.

This goes to show that you can’t force the Java model onto JavaScript without pain. To be successful in JavaScript, you must embrace its object model.

Reflection

It will probably take me a while to get used to JavaScript’s different object model.

spidermanI freaked out when I first realized that any code can change any property and that different instances of a “class” can have different methods.

Coming from a strongly typed world, that seems great power that is easy to abuse.

Better handle that superpower wisely!

Advertisements

Please Join the Discussion

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s