Classical inheritance

Let's talk a bit about classical inheritance. The word "classical" is not used in the sense of old-school middle-age has-been-around forever kind of meaning.

Classical just means that you think in terms of classes. Your objects are created by constructor functions and you want objects created from one constructor Child() to get properties that come from another constructor Parent().

function Parent(){
  this.name = 'Adam';
}
Parent.prototype.say = function(){
  return this.name;
};
 
function Child(){}

So we call a function called inherit() and letís see some patterns for implementing this function.

inherit(Child, Parent);

Option #1 - The ECMA way

Here's the ECMA standard way. You create an object using the Parent() and assign this object to the Child()'s prototype.
Than when you do new Child(), it will get functionality from the Parent() instance via the prototype.

function inherit(C, P) {
  C.prototype = new P();
}

This pattern has the drawback that itís tough to pass arguments to the parent during the call to new Child()

Option #2 - Rent-a-constructor

This next pattern solves the problem of passing arguments. It borrows the parent constructor passing the child object to be bound to this and passing any arguments.

This way you can only inherit properties added to this inside the parent constructor.
You don't inherits stuff that was added to the prototype.

function C(a, c, b, d) {
  P.call(this, arguments);
}

Option #3 - rent + prototype

Combining the previous two patterns you can pass arguments by borrowing the constructor and also inherit from the prototype.

function C(a, c, b, d) {
  P.call(this, arguments);
}
C.prototype = new P();

Drawback is that the parent constructor is called twice.

Option #3

Here's another way to do classical inheritance that doesn't include calling the parent constructor at all.

The rule of thumb is that reusable stuff should go to the prototype.

So for inheritance purposes, anything interesting should be in the prototype. Can you just say that childís is the parentís prototype? Yes, you can.

function inherit(C, P) {
  C.prototype = P.prototype;
}

This gives you really short prototype chain lookups because everybody has the same prototype. But thatís also a drawback because if a child down the inheritance chain modifies the prototype, it affects all parents.

Option #5

This next pattern solves the same-prototype problem by breaking the direct link and benefiting from the prototype chain.

Here you have an empty function which gets the prototype of the parent. The prototype of the child is an instance of the blank function.

function inherit(C, P) {
  var F = function(){};
  F.prototype = P.prototype;
  C.prototype = new F();
}

This pattern has a behavior slightly different from the ECMA standard suggestion because here you only inherit properties of the prototype. And thatís fine, actually preferable. Like I said Ė the prototype is the place for reusable functionality. Anything the parent ads to this inside the constructor is ignored.

Option #5 + super

Building on top of the previous pattern, you can add a reference to the original parent. This is like having access to the superclass in other languages.

The property is called uber because super is a reserved word and superclass may lead the non-suspecting developer down the path of thinking that JavaScript has classes. And we donít want that, right?

function inherit(C, P) {
  var F = function(){};
  F.prototype = P.prototype;
  C.prototype = new F();
  C.uber = P.prototype;
}

Option #5 + super + constructor reset

One last thing to do in this almost perfect classical inheritance function is to reset the pointer to the constructor function in case we need it down the road.

function inherit(C, P) {
  var F = function(){};
  F.prototype = P.prototype;
  C.prototype = new F();
  C.uber = P.prototype;
  C.prototype.constructor = C;
}

If you donít reset the constructor pointer, objects created with the Child() constructor will report that they were created with the Parent() constructor.

A function like this exists in the YUI library and helps you with your classical inheritance needs.

Tags: ,

5 Responses to “Classical inheritance”

  1. JSPatterns.com » Blog Archive » Prototypal inheritance Says:

    [...] Exploring common JavaScript patterns and anti-patterns   « Classical inheritance [...]

  2. JavaScript, l’h√©ritage: Quelle m√©thode ? | Atinux Says:

    [...] Pour pallier √† ce probl√®me, j’ai trouv√© un design pattern int√©ressant sur¬†http://www.jspatterns.com/classical-inheritance/: [...]

  3. Schody Dywanowe Says:

    thanks for posting this info!

  4. Outlet Moncler Says:

    The root of your writing while appearing reasonable in the beginning, did not settle properly with me after some time. Someplace throughout the paragraphs you actually were able to make me a believer unfortunately only for a short while. I still have a problem with your leaps in logic and one would do well to fill in all those breaks. If you can accomplish that, I will definitely be impressed.

  5. Krinkle Says:

    This 5th option is also what nodejs implements as util.inherits:

    https://github.com/joyent/node/blob/v0.9.5/lib/util.js#L537

    Except it fixes one issue with the 5th option, namely that it ensures the constructor property stays non-enumerable.

    Because:

    * function Foo() {}

    At this point Foo.prototype is automatically defined and Foo.prototype.constructor is a non-enumerable property set to Foo.

    * function Bar() {}
    * Bar.prototype = Object.create(Foo.prototype); or: function X() {} X.prototype = Foo.prototype; Bar.prototype = new X();)

    At this point Bar.prototype has no own .constructor property. It inherits the one from Foo.prototype however.

    * Bar.prototype.constructor = Bar;

    This creates an own property on Bar.prototype, which (as properties do by default) will be enumerable.

    The problem with this is that it will be included when iterating over an object in an unfiltered for-in loop.

    Fortunately, as can be seen in the nodejs implementation, one can create it with Object.defineProperty(.., { enumerable: false }) instead. Which fixes the problem.

    However, there is no way to do this in older (pre-ES5) engines.

    Perhaps include that version in this post as Option #6, which fixes the constructor-property-is-enumerable problem of Option #5 (but only works in ES5).

Leave a Reply