JavaScript Inheritance by Example
As Douglas Crockford already pointed out, JavaScript is a class-free, object-oriented language, and uses prototypal inheritance instead of classical inheritance. But what does that actually mean and how does it work in a real example?
Learning by doing
Lets take the following example where we would like to create a constructor function for an object “Vehicle” and then create another function “Car” which should inherit properties from “Vehicle”. We proceed through the following steps:
- Create constructor function for Vehicle
- Create constructor function Car
- Set up dynamic inheritance (this ensures that when you add a property to “Vehicle”, an instance of Car inherits that property)
- Extend Car using prototype
- Create a new instance of Car – my new Audi
- Extend Vehicle using prototype
Step 1: Create constructor function for Vehicle
The first step is very straight forward, what we would like to achieve is setting up the “blueprint” for a Vehicle object, so we define the constructor function like this:
function Vehicle(hasEngine, hasWheels) {
this.hasEngine = hasEngine || false;
this.hasWheels = hasWheels || false;
}
The logical OR operator makes sure that, if no values get passed into that contructor function, the properties hasEngine and hasWheels still have values assigned to them. So that was a piece of cake.
Step 2: Create constructor function Car
Now we would like to do a similar thing for Car. We want to pass in make, model and horsepower of the car and store that in the properties of the object. We do that like:
function Car (make, model, hp) {
this.hp = hp;
this.make = make;
this.model = model;
}
This is basically the same as above, nothing special. We would now create a new Car object by passing in make, model and hp using the new operator. But for now, Car and Vehicle do not have anything to do with each other at all.
Step 3: Set up dynamic inheritance
Now it is getting interesting. What we would like to achieve is to make Vehicle and Car relate to each other in some way. To be specific, Car should be able to inherit properties from Vehicle if we were to extend it (using prototype).
We do this by using the prototype property of Car, like so:
Car.prototype = new Vehicle(true, true);
What JavaScript does when it reaches that statement is the following:
- The new operator creates a generic object and assigns its __proto__ property to Vehicle.prototype
- The new operator passes the object to the Vehicle constructor function as the this keyword.
- The object gets the properties hasEngine and hasWheels assigned
- Upon return from the constructor, the object gets assigned to Car.prototype
What we have set up here is an inheritance chain from Vehicle to Car. The prototype property of Car now points to the abovementioned generic object, which consists of the following properties:
- hasEngine
- hasWheels
- __proto__ (which points to Vehicle.prototype which is empty at this point)
We will make use of that construct in Step 6.
Step 4: Extending Car using prototype
We would like to extend the capabilities of Car by a function displaySpecs, which should log the details of the car to the console using console.log. We do this using prototype:
Car.prototype.displaySpecs = function () {
console.log(this.make + ", " + this.model + ", " + this.hp + ", " + this.hasEngine + ", " + this.hasWheels);
}
We have extended the functionality of Car by adding another function to the abovementioned object which is referenced using prototype. That object now holds the following:
- hasEngine
- hasWheels
- displaySpecs
- __proto__ (which points to Vehicle.prototype which is still empty)
Step 5: Creating a new Car
Now we have set up the foundations in order to proceed with creating an instance of Car. Let us make it an Audi, just to be posh.
var myAudi = new Car ("Audi", "A4", 150);
We can go through it again step by step:
- The new operator creates a generic object and sets its __proto__ property to Car.prototype
- The new operator passes that object to the Car constructor function as the this keyword
- The object gets hp, make and model properties with the values passed in assigned
- Upon return from the constructor function, the object gets assigned to the myAudi variable
This means that the newly created instance of Car has the following properties:
- hp: 150
- make: “Audi”
- model: “A4″
- __proto__ (pointing to Car.prototype, which holds hasEngine, hasWheels and displaySpecs)
If we now call displaySpecs, it will output the properties of myAudi to the console:
myAudi.displaySpecs(); // logs: Audi, A4, 150, true, true
This works because of the way JavaScript looks for the availability for properties of objects:
- First, it checks whether displaySpecs is available in myAudi (which it is not)
- Next, it goes up the inheritance chain using __proto__ which points to Car.protoype
- Car.prototype holds displaySpecs, so the function gets executed in with the scope of myAudi. This means that within displaySpecs, the keyword this refers to myAudi object. Consequently, it prints out the properties of myAudi (“Audi”, “A4″ etc.)
Step 6: Extend Vehicle using prototype
Now you might decide that you would like to extend the original capabilities of Vehicle. Using prototype lets us do that, so if we would want, for some reason, to add a hasTrunk property, we can do that like so:
Vehicle.prototype.hasTrunk = true;
Because we have set up dynamic inheritance, this makes sure that the object we created, myAudi, inherits that property. If we were to output that property to the console, we will see that this has worked:
console.log(myAudi.hasTrunk); // logs: true
This is, again, due to the inheritance chain we have set up:
- Is hasTrunk available in myAudi itself: no
- Is hasTrunk available in Car.prototype: no
- Is hasTrunk available in Vehicle.prototype: yes
JavaScript has worked itself up the inheritance chain and then logs the value of hasTrunk to the console. If hasTrunk is not found, we would get “undefined” in the console.
Sidenote & Conclusion
If you would want to avoid passing in properties to the Vehicle constructor when setting up the inheritance chain, you can call the Vehicle constructor from within the Car constructor like so:
function Car (make, model, hp) {
this.base = Vehicle;
this.base(true, true);
this.hp = hp;
this.make = make;
this.model = model;
}
and setting up dynamic inheritance like this:
Car.prototype = new Vehicle;
Now everytime a new Car instance gets created, the Vehicle constructor is invoked and hasEngine and hasWheels are set to true and set as properties of the newly created object. Details about that can be found at the Mozilla Developer Center.
To me, this little example made things a lot clearer and especially having some sort of visual image helps a lot of grasping what is going on and how inheritance within JavaScript works. In order to think through the example again, please have a look at the demo page (in Firefox with Firebug opened on the console tab) for this little example.
Resources
- Core JavaScript 1.5 Guide at Mozilla Developer Center (MDC)
- The Employee Example at MDC which explains in further detail how inheritance works
- Kevin Lindsey’s approach to simulating object-oriented programming in JavaScript
- Harry Fuecks from SitePoint on JavaScript inheritance
Categories
- California (3)
- Cars (1)
- Cologne (3)
- Cycling (7)
- Design (1)
- Events (10)
- Gadgets (1)
- Hockey (1)
- Job (12)
- Life in General (19)
- London (7)
- Mobile (8)
- Music (3)
- Guitar (3)
- NYC (11)
- Programming (6)
- Radio (1)
- Shopping (1)
- Travel (21)
- Uncategorized (1)
- Video (1)
- Web (56)
- Coding (37)
- Agent YUI (6)
- Performance (5)
- Design (2)
- Coding (37)
Blogroll
- Larry Halff
- Drew McLellan
- Ben Ward
- Tantek Çelik
- Tara Hunt
- Marianne Masculino
- Frederique Dame
- Tom Hughes-Croucher
- Andy Clarke
- Tim Huegdon
Recent Posts
- Boost the performance of your mobile web app using YUI Compressor
- Cycling through Death Valley, powered by REI
- Bike ride up (and down) a volcano
- Text to speech functionality in web applications using the iSpeech API
- Search along a route with CloudMade’s Local Search API