A Touch of Class

Inheritance in JavaScript

MARK DALGLESH:
@markdalgleish

Engineer @ Aconex
Organiser @ MelbJS

JS looks like it has classes...

var charlie = new Actor();

But JS doesn't have classes???

function Actor(name) {
  this.name = name;
}

Actor.prototype.act =
  function(line) {
    alert(line);
  };
function SilentActor(name) {
  this.name = name;
}

SilentActor.prototype =
  new Actor();

SilentActor.prototype.canSpeak
  = false;

Let's travel to the year 1995...

"Java applets are the future"
-1995 dude

Mocha
became LiveScript
became JavaScript

Looks like Java. Tastes like Scheme mixed with Self.

Classical inheritance:

Class => Instance

Prototypal inheritance:

Object => Object

You must unlearn what you have learned.

ES5 Inheritance:

Object.create(parentObject);
var actor = {
  canAct: true
};

var busterKeaton =
  Object.create(actor);
var actor = {
  canAct: true,
  canSpeak: true
};

var silentActor =
  Object.create(actor);

silentActor.canSpeak = false;
var busterKeaton =
  Object.create(silentActor);

busterKeaton.canAct?

Find first occurence of property (does it exist?)

 -- busterKeaton?    NO
  |-- silentActor?   NO
    |-- actor?       YES (true)

Differential inheritance

All silent actors lose their jobs?



silentActor.isEmployed = false;

busterKeaton.isEmployed; // false

ES5 Prototype Reflection:

Object.getPrototypeOf(busterKeaton);

// -> silentActor

"Where's super?"

Any function can run in any context

silentActor.act =
  function(line) {
    
    // Super:
    actor.act.call(this, line);
  
  }

"Where are my constructors?"

DIY Constructor:


function makeActor(name) {
  var a = Object.create(actor);
  a.name = name;
  return a;
}

Understanding prototypes with a touch of class...

Let's revisit our simple example:

var charlie = new Actor();

"Actor" is just a regular function...

function Actor() {}
function Actor(name) {
  this.name = name;
}

It's used as a constructor when invoked with "new"

Any function can
be used as a constructor...

"charlie" inherits from the object at "Actor.prototype"



var charlie = new Actor();

Yes, functions can have properties.

function Actor(){}

// We can set any property:
Actor.foo = 'bar';
Actor.abc = [1,2,3];

// Used for constructors:
Actor.prototype = {foo: 'bar'};

Since any function
can be used as a constructor...

...all functions
have a "prototype" property
(just in case)

alert("is a function, so...");

typeof alert.prototype
  === 'object'; // true

"What about multiple inheritance?"

A function's prototype property inherit from another:

SilentActor.prototype =
  Object.create(Actor.prototype);

// We can now augment the prototype:
SilentActor.prototype
  .canSpeak = false;

Instantiating an object does 3 things...

var charlie = new Actor()
  1. Create new object (charlie) that inherits from object at Actor.prototype
  2. Run the "Actor" function against charlie
  3. Return charlie

Sound familiar?

function makeActor(name) {
  var a = Object.create(actor);
  a.name = name;
  return a;
}
var busterKeaton =
  new SilentActor();

busterKeaton.canAct?

Find first occurence of property
(does it exist?)

 -- busterKeaton?              NO
  |-- SilentActor.prototype?   NO
    |-- Actor.prototype?       YES

Want class syntax today?

"Class",
"Klass",
etc.

var Actor = Class.extend({
  init: function(name) {
    this.name = name;
  },

  act: function(line) {
    alert(this.name + ': ' + line);
  }
});

CoffeeScript has classes...

class SilentActor extends Actor

  canSpeak: false

  act: (line) ->
    alert("#{@name}: #{line}")

TypeScript has classes...

class SilentActor extends Actor {

  public canSpeak = false;

  public act(line: string) {
    alert(this.name + ': ' + line);
  }

}

But it all boils down to prototypes

Want native class syntax?

The current ES6 draft has classes...

class SilentActor extends Actor {

  canSpeak = false;

  act(line) {
    alert(this.name + ': ' + line);
  }

}

But, they too are just sugar...

We've covered a bit of ground...

For a recap, check out my article at markdalgleish.com

@markdalgleish