JavaScript Inheritance Revisited - part 2

On the first part of this blog post I revisited the inheritance notion.

This post focuses on what other frameworks and compiled-to-js languages are doing in terms of inheritance.

 

What are the others doing?

We can always learn a good deal by examining what other popular frameworks are doing. In recent years several client side helper frameworks and various scripting languages that compile into JavaScript were developed. These scripting languages provide inherent syntax for creating classes and inheritance.

 

Underscore extend  and multiple inheritance

Perhaps the most interesting case is the Underscore implementation of inheritance with a different flavor. Underscore is a library of common manipulations on objects and collections in JavaScript. It is used in various projects (e.g. Backbone ) and is available as an NPM package for Node.js. The idea behind the extendfunction is simple. When extending one object with the other it simply copies all the properties and functions from the other object to the target object. In other words, it applies all the behavior and data of the other object onto the target object. This is possible due to the fact that when you use this in a JavaScript function you refer to the caller and not the definer of the function. This allows you to use the mixin pattern where you can apply multiple behaviors to the same object, which is sort of like multiple inheritance. I know this is not inheritance in the sense where you have a parent object which supplies some functionality and a child object which can override that functionality but also call the parent object functionality. In the case of extend you lose the link to the parent but you gain the ability to apply several behaviors to the same object. Here is an example:

var named = {
    name: "John",
    sayName: function(){
        alert(this.name);
    }
}

var smith = _.extend({}, named);
smith.sayName(); //John
smith.name = "Smith";
smith.sayName(); //Smith

 CoffeeScript

CoffeeScript is a very popular scripting language by Jeremy Ashkenas (@jashkenas) et. al which aims to resolve the "issues" in JavaScript. This scripting language contains a built in mechanism for inheritance. Let's look at an example of a CoffeeScript script and its compiled version in JavaScript:

class Parent
  foo: ->
    alert "Parent foo"

class Child extends Parent
  foo: ->
    alert "Child foo"
    super 

item = new Child
item.foo()

compiles into

var Child, Parent, item, _ref,
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { 
      for (var key in parent) { 
          if (__hasProp.call(parent, key)) 
            child[key] = parent[key]; 
        } 

        function ctor() { 
            this.constructor = child; 
        } 

        ctor.prototype = parent.prototype; 
        child.prototype = new ctor(); 
        child.__super__ = parent.prototype; 
        return child; 
    };

Parent = (function() {
  function Parent() {}

  Parent.prototype.foo = function() {
    return alert("Parent foo");
  };

  return Parent;

})();

Child = (function(_super) {
  __extends(Child, _super);

  function Child() {
    _ref = Child.__super__.constructor.apply(this, arguments);
    return _ref;
  }

  Child.prototype.foo = function() {
    alert("Child foo");
    return Child.__super__.foo.apply(this, arguments);
  };

  return Child;

})(Parent);

item = new Child;

item.foo();

As you can see the inheritance implementation is similar to the original aforementioned JavaScript code, the main difference is perhaps the handling of the super call. CoffeeScript does something more sophisticated by providing a "constructor" functionality. It doesn't use the regular JavaScript prototype (__proto__) chain but instead creates a __super__ property which points to the prototype of the parent. It also copies the "static" properties from the parent to the child which we didn't do.

 

TypeScript

TypeScript was recently introduced by Microsoft as a superset of JavaScript. It is a new scripting language which adds type safety and inference, yet it supports the original syntax of JavaScript (actually it adds much more, you are welcome to explore). TypeScript was developed by Anders Hejlsberg (@ahejlsberg), (Yes! This is the same guy who gave us Turbo Pascal, Delphi, and C#) and it has built-in support for classes and inheritance. Here is the code from the CoffeeScript example written with TypeScript:

class Parent {
    foo (){
        alert("Parent foo");
    } 
}

class Child extends Parent{
    foo(){
        alert("Child foo");
        super.foo();
    }
}

item = new Child;
item.foo();

compiles into

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var Parent = (function () {
    function Parent() { }
    Parent.prototype.foo = function () {
        alert("Parent foo");
    };
    return Parent;
})();
var Child = (function (_super) {
    __extends(Child, _super);
    function Child() {
        _super.apply(this, arguments);

    }
    Child.prototype.foo = function () {
        alert("Child foo");
        _super.prototype.foo.call(this);
    };
    return Child;
})(Parent);
item = new Child();
item.foo();

The approach is slightly different but the key ideas remain the same. Create a function which generates the prototype for the constructor object. Then, link that prototype into the prototype chain of the parent. Note that the__extends function that the TypeScript compiler adds to the compiled code, sets the constructor of the objects generated by the constructor function (conveniently named __) to the original function (in the example it isChild).

 

Conclusion

The key takeaway is that JavaScript supports inheritance in an easy and convenient way. If you are not awkward with prototypical inheritance then the simple inheritance model that JavaScript provides will be very simple and adequate for any use. The issues mostly start when trying to apply practices from class based inheritance into the realm of prototypical inheritance. When you choose this path you have tools and frameworks that make your life a lot easier and you don't need to deal with all the pain of abusing the prototypical inheritance scheme into the class inheritance scheme. The nicest thing about JavaScript as a language is that it allows you to choose the path most comfortable for you.

Good luck on your next JavaScript project, Boris

 

 

This post has been written by Boris Kozorovitzky

Labels: web
Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
About the Author


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation