Is your 401k bleeding money by not offering low-cost index funds? Now there is a way to find out.
GreaterThanZero.com


Page 7 of: Object-Oriented Programming in JavaScript Explained, by Thomas Becker   about me  

Inheritance

Continuing with the example of the class Point of the previous section, suppose we want to extend the capabilities of a point by giving it an annotation that shows up in the label. We can achieve that by deriving a class AnnotatedPoint from Point. This may not be a shining example of object-oriented analysis and design, but for the sake of illustration, please humor me.
class AnnotatedPoint extends Point {

  // Annotation, to be displayed in the label
  private String annotation;

  // Constructor from coordinates and annotation
  public AnnotatedPoint(double x, double y, String annotation) {
    super(x, y);
    this.annotation = annotation;
  }

  // Get label (coordinates + annotation)
  public String getLabel() {
    return new StringBuilder(super.getLabel())
      .append(" ").append(annotation).toString();
  }
}
To see how this is done in JavaScript, let us first look at the constructor function for annotated points:
function AnnotatedPoint(x, y, annotation) {
  Point.call(this, x, y);
  this.annotation = annotation;
}
When this constructor function is called with the new operator, as in
var p = new AnnotatedPoint(0.0, 0.0, "Origin");
the first thing that happens is that a new object is created, and this is bound to that object. Then the function body is executed. The first thing that needs to happen in the function body is to initialize the AnnotatedPoint instantiation as a Point instantiation. That's what the call to the constructor function Point does: it performs its initialization work with this bound to our new annotated point. This is why it's important that constructor functions can be called not only with the new operator, which causes them to create a new object, but also as ordinary functions, with this bound to an already existing object. The rest of AnnotatedPoint's body is obvious: create and initialize the additional field annotation.

So far, we have made sure that an annotated point inherits all instance properties, that is, all non-static fields, of a point. Next, we need to ensure that annotated points inherit the methods and static fields of points, that is, the things that are stored in Point.prototype. This is achieved by inserting Point.prototype into the property lookup chain for annotated points. Figure 3 shows what we need to achieve.

If you know that your JavaScript program will run in an environment that supports the ECMAScript Version 5 standard, then the method Object.create is the right tool for the job. When this method is called with one argument, as in

var newObject = Object.create(proto);
it creates and returns a new object with proto as the first prototype in its property lookup chain. Therefore, we can insert Point.prototype into the property lookup chain for annotated points with just this one line of code:
AnnotatedPoint.prototype = Object.create(Point.prototype);
This will result in the correct property lookup chain for AnnotatedPoint as shown in Figure 3. Notice that the linking mechanism of the property lookup chain is an implementation detail that is handled by Object.create. We do not and should not know how it's done.

If you need to support pre-ES5 legacy environments as well, then you have to check if Object.create is defined, and if not, fall back on the legacy way of creating the inheriting prototype. One way of doing it would be:

AnnotatedPoint.prototype = makeDerivedPrototype(Point.prototype);
where the function makeDerivedPrototype is defined as
function makeDerivedPrototype(basePrototype) {
  var derivedPrototype = null;
  if(Object.create !== undefined) {
    derivedPrototype = Object.create(basePrototype);
  } else {
    function EmptyObject(){}
    EmptyObject.prototype = basePrototype;
    derivedPrototype = new EmptyObject();
  }
  return derivedPrototype;
}  
It is a worthwhile exercise to convince yourself that the legacy code above results in the proper lookup chain for annotated points; however, you may not want to think about it at all as the preferred way is to delegate everything to the method Object.create.

To complete the AnnotatedPoint setup, it remains to place the additional methods and static variables, if any, into the prototype AnnotatedPoint.prototype. In our case, there is just one additional method, namely, getLabel, and it happens to override an existing method of Point by the same name. Moreover, the overriding version must call the the base version of getLabel. This is how it's done in JavaScript:

AnnotatedPoint.prototype.getLabel = function() {
  var pointLabel = Point.prototype.getLabel.call(this);
  return pointLabel + " " + this.annotation;
};
For annotated points, the above version of getLabel will override the getLabel method of Point simply because AnnotatedPoint.prototype comes before Point.prototype in the property lookup chain. The call to the base version in the first line of the function body deserves some attention. Here, we must make sure that the receiver of the call is the the annotated point whose label we are retrieving. Since the method is overridden, the only way to achieve that is to set the receiver explicitly via the call method. Had we decided to make a whole new method, say, getAnnotatedLabel, rather than overriding getLabel, the code would have been a bit simpler:
AnnotatedPoint.prototype.getAnnotatedLabel = function() {
  return this.getLabel() + " " + this.annotation;
};
Here's the complete JavaScript implementation of AnnotatedPoint:
//
// The equivalent of the AnnotatedPoint class in JavaScript
//

function AnnotatedPoint(x, y, annotation) {
  Point.call(this, x, y);
  this.annotation = annotation;
}

// ES5 only
AnnotatedPoint.prototype = Object.create(Point.prototype);

AnnotatedPoint.prototype.getLabel = function() {
  var pointLabel = Point.prototype.getLabel.call(this);
  return pointLabel + " " + this.annotation;
};
In object-oriented programming, an inheritance relation constitutes an "is-a" relationship: every annotated point is a point. JavaScript's instanceof operator recognizes that:
var ap = new AnnotatedPoint(0.0, 0.0, "Origin");
ap instanceof AnnotatedPoint; //true 
ap instanceof Point; //true