JavaScript 101

26 May 2013. comments

Inheritance

Javascript is an interesting language. It doesn’t follow a class-based inheritance model like most modern languages and instead is based on prototypal inheritance. Basically that means that objects inherit from other objects, rather than object blueprints inheriting from other object blueprints. When method calls occur the current object is inspected for the method. If the method isn’t found, we walk up the prototype chain until it is found, or we bottom out at the root object. We can apply a method to all objects that inherit down the chain by adding the method to an object’s prototype. All of this means that it doesn’t matter what an object’s lineage is, only what it can do at the moment, and that can change.

var Bar = function(name) {  
  this.name;  
};  
Bar.prototype = new Foo();  
var barInstance = new Bar();  
// barInstance has all the stuff from Bar as well as Foo.  

“This”

One of the most interesting and dangerous parts of javascript is this. The reason its so dangerous is because its behavior is different based on what context you are talking about.

  • When functions are invoked as methods on an object (‘.’ refinement), this is bound to the object.
  • When functions are invoked without . refinement, this is bound to the global object, regardless of what scope this happens in.
  • When functions are invoked with new keyword prefix:
    1. A new object is created.
    2. The new object’s internal prototype is set to the function’s prototype property.
    3. this is bound to that new object.
    4. The function is executed.
  • When functions are invoked with apply, you can choose this and the parameters.

Constructors

The new keyword in javascript exists to create objects with their prototype pre-configured. What this means is that if you have a function:

var Ctor = function(options) {  
  this.name = options.name;  
};  

And then execute that function like this:

var obj = new Ctor({ name: 'Ben' });

This will result in the following taking place:

  1. A new object will be created with it’s hidden prototype link set to Ctor’s prototype property, if it exists. If none was defined by us a new object will be created for us to fill the role.
  2. Context this will be set to that object.
  3. Ctor will execute, allowing us to make modifications to this.

So in the previous code snippet obj would be a new object whose prototype is Ctor.prototype and modified by us to have a name property.

The other bit that I didn’t mention is that Ctor’s default prototype also contains a property called constructor which is set to Ctor itself. In other words this is true:

Ctor.prototype.constructor == Ctor

This is all fine but once we dive into inheritance things get messy fast. Because of the prototypal inheritance of javascript we have to manually set up parent/child relationships and in order to do that we have to be able to control what this means during the construction of new objects from the child constructor. This will probably just be clear if I show you the annotated code:

var Parent = (function() {

  // define our parent constructor
  var ParentConstructor = function(options) {
    this.name = options && options.name;
  };

  // set up some methods that will be available to all
  ParentConstructor.prototype = {
    getName: function() {
      return this.name;
    }
  };

  // it's all ready to go, so lets stick it into 'Parent'
  return ParentConstructor;

})();

var Child = (function() {

  // define our child constructor
  var ChildConstructor = function(options) {

    // before we start doing the child constructor modifications 
    // to 'this', we want to inherit the parent's constructor
    // we have to manually call the parent constructor, telling 
    // it to use the newly created 'this' object from the execution 
    // of 'new Child' as discussed before:
    Parent.call(this, options); 

    // make some differential modifications ourself in child
    this.sound = options.sound;

  };

  // the only way in javascript to control what 'prototype' is going   
  // to be (in other words, to make us inherit one in particular) you   
  // have to use the 'new' keyword. So we make a dummy constructor   
  // function and set up the prototype to the parent's.  
  var ProtoConstructor = function() {};  
  ProtoConstructor.prototype = Parent.prototype;  
  ChildConstructor.prototype = new ProtoConstructor();  

  // we also want to do the following because the above doesnt set us up to   
  // where 'constructor' is the correct child constructor.  
  ChildConstructor.prototype.constructor = ChildConstructor;  

  // define some method on child  
  ChildConstructor.prototype.getSound = function() {  
    return this.sound;  
  };  

  // ok its ready to go so let's stick it into 'Child'  
  return ChildConstructor;  

})();  

var parentInstance = new Parent({ name: 'Bob' });  
console.log(parentInstance.getName()); // Bob  

var childInstance = new Child({ name: 'Joe', sound: 'Burp' });  
console.log(childInstance.getName() + ' goes ' + childInstance.getSound()); // Joe goes Burp  

comments

Tagged: javascript

2017 Ben Lakey

The words here do not reflect those of my employer.