Patterns of Code Reuse

Notes

We will discuss in this section the main ways one can share functionality in Javascript. They are:

Inheritance

Class inheritance is a fundamental component of object-oriented programming, but must be used with care and when appropriate. The key notion is that of a subclass, whereupon a class can inherit all of another class’s properties and methods, and override those that it needs to or add new methods. The subclassing relation is often called an “is a” relation.

We will take a look at an example in a minute, but first of all, let us discuss a “newClass” method we will start using, which “creates” a new class.

function newClass(init, superclass) {
   var cls, proto;

   init = init || function() {};
   superclass = superclass || Object;
   proto = Object.create(superclass.prototype);
   cls = Object.create({}, {
      "prototype": { value: proto },
      "super": { value: superclass }
   });
   Object.defineProperty(cls.prototype, "class", { value: cls });
   cls.initialize = init;
   cls.new = function newObj() {
      var o = Object.create(proto);
      cls.initialize.apply(o, arguments);
      return o;
   };

   return cls;
}

This allows us to create classes and have them inherit from other classes.

The basic construction for inheritance would go something like this:

var Point = newClass(function init(x, y) {
   this.x = x;
   this.y = y;
});
Point.prototype.r = function() {
   return Math.sqrt(this.x * this.x + this.y * this.y);
};

var ColorPoint = newClass(
   function init(x, y, color) {
      Point.initialize.call(this, x, y);
      this.color = color;
   },
   Point  // <---- ColorPoint inherits from Point
);
ColorPoint.prototype.getColor = function() {
      return this.color;
};

var p1 = Point.new(2,3);
p1.x;
p1.y;
p1.r();

var p2 = ColorPoint.new(2, 3, "blue");
p2.x;
p2.y;
p2.r();
p2.getColor();

We set a class to inherit from another by passing the superclass as the second argument in newClass. Effectively this makes our subclass’s prototype inherit from the superclass’ prototype. So in our example, the ColorPoint class inherits all methods of the Point class.

This is then one way to “reuse code”: The subclass’ objects can reuse the methods of the superclass’, provided the don’t override them.

Disadvantages:

Composition and Delegation

In object composition one object holds in its instance variables a reference to another object, and accesses it that way. It might optionally create methods that turn around and call methods of the composed object:

var ColorPoint = newClass(function init(x, y, color) {
      this.point = Point.new(x, y);
      this.color = color;
});
ColorPoint.prototype.setPoint = function(p) {
   this.point = p;
   return this;
};
// delegate calls to point
ColorPoint.prototype.getx = function() { return this.point.x; };
// Or even transparently
Object.defineProperty(ColorPoint.prototype, "x", {
   enumerable: true,
   get: function() { return this.point.x; },
   set: function(v) { this.point.x = v; return this; }
});

Advantages:

Disadvantages:

One of our design principles is to favor composition over inheritance, as it tends to create more flexible designs.

Mixins

There is another possibility that can be helpful some times, called a mixin. It is typically used when you want to reuse some functionality. For instance, we can “mix in” the iterator prototype methods into an object with “next” and “hasNext” methods.

Mixing in involves directly copying all the methods from one or more objects into another, with something like this:

function mixin(target) {
   Array.prototype.slice.call(arguments, 1)
      .forEach(function(source) {
         Object.keys(source).forEach(function(key) {
            target[key] = source[key];
         });
      });

   return target;
}

Disadvantages: