Classes, Inheritance And Mixins In JavaScript

JavaScript doesn’t have native support for classes. Functions can be used to simulate classes (somewhat), but in general JavaScript is a class-less language. Everything is an object. This makes inheritance particularly strange, since objects inherit from objects, not classes from classes as in languages like Ruby or Java.

CoffeeScript does have support for classes though - that is to say, class is a keyword. So how does that work, if JavaScript doesn’t have classes? (CoffeeScript just compiles down to JavaScript, so it’s not a superset of the language - it merely adds some syntactical sugar)

Classes in CoffeeScript and JavaScript

This CoffeeScript ‘class’

class Person
  constructor: (@name, @birthday) ->

  age: ->
    moment().diff(@birthday, 'years')

is equivalent to this JavaScript:

Person = (function() {
  function Person(name1, birthday1) {
    this.name = name1;
    this.birthday = birthday1;
  }

  Person.prototype.age = function() {
    return moment().diff(this.birthday, 'years');
  };

  return Person;
})();

This is one pattern for defining classes in JavaScript - creating a named function and adding methods to the prototype of the function. (The other two common patterns are using an object literal and declaring a singleton function)

When defining a class with this pattern (either in CoffeeScript or JavaScript) the function actually behaves very similar to classes in other languages.

bob = new Person("Bob", new Date(1980,4,1))
bob.name   # Bob
bob.age()  # 34

(The JavaScript for this code looks exactly the same, just add some semicolons!)

CoffeeScript also allows us to have inheritance between classes. So how does that work, if we just said that JavaScript doesn’t really have classes (just functions) and that objects inherit from objects, not classes from classes?

Class Inheritance in CoffeeScript and JavaScript

Let’s add some inheritance to our CoffeeScript ‘class’.

class Person
  constructor: (@name, @birthday) ->

  age: ->
    moment().diff(@birthday, 'years')

class Employee extends Person
  constructor: (name, birthday, @company) ->
    super(name, birthday)

  email: ->
    "#{@name}@#{@company.replace(/ /g,"")}.com".toLowerCase()

Here is the equivalent JavaScript (I have reformatted the output from the CoffeeScript compiler to make it a bit more readable).

var extend = 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;
}
var hasProp = {}.hasOwnProperty;

var Person = (function() {
  function Person(name1, birthday1) {
    this.name = name1;
    this.birthday = birthday1;
  }

  Person.prototype.age = function() {
    return moment().diff(this.birthday, 'years');
  };

  return Person;

})();

var Employee = (function(superClass) {
  extend(Employee, superClass);

  function Employee(name, birthday, company) {
    this.company = company;
    Employee.__super__.constructor.call(this, name, birthday);
  }

  Employee.prototype.email = function() {
    return (this.name + "@" + (this.company.replace(/ /g, "")) + ".com").toLowerCase();
  };

  return Employee;

})(Person);

That is some pretty intense JavaScript, but the magic is happening in the extend function. There are 2 types of methods that need to be inherited from the parent - methods defined on the parent itself (we can think of these as class methods) and methods defined on the prototype of the parent (we can think of these as instance methods).

The first thing that happens is that all the class methods on the parent are added to the child (note - not to the prototype of the child). This concept is pretty simple - since functions in JavaScript are objects, we can effectively loop through all the functions of the parent class (function) and add them to the child class (function).

for (var key in parent) {
  if (hasProp.call(parent, key)) child[key] = parent[key];
}

A special constructor function (called ctor) is then created to act as the prototype of the child class. CoffeeScript does a lot of work here just to enable us to call super when we override parent methods in the child. (super is treated as a keyword and CoffeeScript does some additional magic in order to make it all work)

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

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

As I mentioned earlier, in JavaScript objects inherit from objects (instead of classes from classes). We can therefore set the prototype for the child class (function) to an instance of the ctor function - because they’re both just objects. Any new instances of the child class will also have the same prototype and because the ctor function has the parent class as a prototype we are effectively using normal prototypal inheritance with an additional level of indirection - all to enable calls to super (which is actually really useful).

employee = new Employee("Steve", new Date(1975,7,15), "JavaScript Inc")
employee.name     # Steve
employee.age()    # 39
employee.email()  # steve@javascriptinc.com

So if CoffeeScript (and as a result, JavaScript) can fake classes with functions and inheritance with functions, could it fake mixins?

Mixins in CoffeeScript and JavaScript

In order to implement mixins in CoffeeScript (or JavaScript), we would effectively need to copy the functions from one class (function) into another. As we have seen with the extend function that CoffeeScript uses for inheritance, copying functions between classes is actually rather easy.

I’m referring to the first part of the extend function:

for (var key in parent) {
  if (hasProp.call(parent, key)) child[key] = parent[key];
}

Let’s look at how we might implement mixins in CoffeeScript:

class Person
  constructor: (@name, @birthday) ->

  age: ->
    moment().diff(@birthday, 'years')

class Manager
  @title: ->
    "Mr. #{@name}"

class Employee extends Person
  for key of Manager
    @::[key] = Manager[key] if Manager.hasOwnProperty(key)

  constructor: (name, birthday, @company) ->
    super(name, birthday)

  email: ->
    "#{@name}@#{@company.replace(/ /g,"")}.com".toLowerCase()

Within the Employee class (function) I am simply looping through all the properties on the Manager class (function) and assigning them to the prototype of the Employee class.

This might seem strange until you consider that this is exactly how we have been declaring methods on classes all along!

class Person
  constructor: (@name, @birthday) ->

  age: ->
    moment().diff(@birthday, 'years')

In this example the age method compiles down to this JavaScript:

Person.prototype.age = function() {
  return moment().diff(this.birthday, 'years');
};

This is equivalent to how I am assigning the mixin methods - this piece of CoffeeScript:

Person::age = ->
  moment().diff(@birthday, 'years')

Is again equivalent to the same JavaScript:

Person.prototype.age = function() {
  return moment().diff(this.birthday, 'years');
};

So this pattern really allows us to assign methods to our ‘class’ in exactly the same way as we did before. The only difference is that our Manager ‘class’ (in Ruby we would call this a module) assigns functions directly to the Manager ‘class’, instead of to the propotype. This makes perfect sense, since we don’t necessarily want to have to create an instance of the Manager ‘class’ in order to use it as a mixin in the Employee ‘class’.

There is a very strong similarity to how mixins behave in Ruby, except this is actually a bit cleaner. In Ruby a mixin is actually injected in the inheritance chain, where in JavaScript the function is directly assigned to the class.

Why do we need Mixins?

I started playing around with mixins in CoffeeScript because I became frustrated with sharing code via inheritance. When the only way to share code between classes is inheritance, you can easily end up with inheritance where it doesn’t really make sense. (When all you have is a hammer, everything looks like a nail!)

This pattern would at the very least provide an alternative to inheritance - which is clearly not always the best solution. You can find all the code on Plunker. Happy coding.