In part 9 of this series
Naked Javascript, I am going to talk discuss inheritance in Javascript. In order to understand the concept of inheritance, it definitely hepls to understand the concept behind functions and closures in Javascript. You can check out the previous article on closures and this article on functions to brush up on the basics of these concepts.
Inheritance in Javascript is a bit different from inheritance in other programming languages like Java. The difference lies not only in terms of the syntax but even the implementation is so staggeringly different that it may sometimes not be an easy task to grasp without knowing a few other concepts in javascript.
Lets start from the root of the problem.
Functions
As I have mentioned multiple times throughout the series, functions are first class objects in Javascript. The underlying reason is simple. In Javascript, everything is an instance of the class Object. This is somethign that is taken from Java. The Object class provides a default implementation of several commonly used functions, for example the 'toString' function. Now, to think about it, since all javascript objects need this functionality, they must somehow inherit this function from the object class.
There are 2 important things that you need to remember when dealing with inheritance in JavaScript.
1) Since JavaScript does not have classes, inheritance in JavaScript is achieved by The
2) The mechanism by which two objects are connected in an inheritance hierarch is via the 'prototype' property of objects.
Lets talk about 'prototype'.
When you create any Function in javascript, javascript implicitly defines a new property on that object called 'prototype'. The prototype property is no ordinary property.
Lets see it with a simple example
//Here I have defined a simple constructor function
function Employee(name){
this.name=name;
}
//Set the default age
Employee.prototype.age = 16;
var emp1 = new Employee('xyz');
console.log(emp1.age);
This is the simplest example that demonstrates the use of the prototype property.
In the first few lines, I defined an ordiary function constructor that would let me create Employee objects. We know that functions are objects. And as I stated earlier, every object gets its own prototype property. Therefore we were able to define a variable called 'age' on the prototype property of the employee class and set its value to 16.
In the last few lines, notice that although we did not declare any property called 'age' on the Employee instance inside the Employee constructor function, we did not get an 'undefined' when we printed the value of the 'age' property on the console. The reason this happened is because we defined a property called 'age' on the prototype property of the Employee function.
What happend here is that when an attempt was made to access a property that is not present on the 'empl' object, the prototype of the constructor function was searched for the property of the same name. So in this case, when we accessed emp1.age, since the JavaScript engine was unable to find the property on the object itself, it tried to lookup a property called 'age' on the Employee.prototype object. This leads us to the conclusion that prototypes are a good place to declare defaults.
Now lets see what happens when we attempt to modify a property on an object that was defined only on the prototype.
function Employee(name){
this.name=name;
}
//Set the default age
Employee.prototype.age = 16;
var emp1 = new Employee('xyz');
var emp2 = new Employee('abc');
console.log(emp1.age);
emp1.age++;
console.log(emp2.age);
console.log(emp1.age);
When you check the console, you will notice that once you modify the property on the object, that was initially defined only on the prototype, the object will then begin to own a local copy of the property. In our case, when we wrote emp1.age++, this is what we would normally expect the program to do : -
emp1.age = emp1.age + 1;
But, this is what the js engine executed
emp1.age = Employee.prototype.age + 1;
Thats because, before adding doing the addition, emp1.age does not exist. And thats the reason why, on the RHS of the equals operator, emp1.age becomes Employee.prototype.age
The direct implication of this feature is that the prototype property of Functions becomes the most suitable candidate for declaring constants and other properties that will not be modified by instances created by that funcion but which are required for every instace of the class.
function Employee(name){
this.name=name;
}
Employee.prototype.getName=function(){
return this.name;
}
This solved the problem of creating a single place to define functions that can be reused by all instances.
However, what if you want a function or a property to be available for all functions?
The answer is pretty simple. Define the property/function on the Function.prototype. For example.
Function.prototype.PI=3.14;
var dummyFunction = function (){};
console.log(dummyFunction.PI);
Note the syntax that we used to access the property PI. We directly made use of the function name. Reiterating myself, this is possible because every function is an object in itself whose constructor is the Function method.
Inheritance
Inheritance is a little bit twisted in Javascript as compared to Java and other similar languages. Thats because in Javascript, objects inherit from objects. The prototype property plays a key role in implementing inheritance. However, it can also be a bit misleading at times. Consider the following example.
In this example, we want to create a Class whose objects inherit certain protperties from another Object. The simplest example to represent this form of inheritance would be
//The doom object is the parent object
//You want to inherit its functions and properties in all
//instances of the MyClass class
var doom = {name:'doom',
baseMethod:function(){
console.log('defined on doom');}
};
//Your class whose instances you want to create
function MyClass(){
//Some properties
}
//Set the prototype property to the 'doom' object
MyClass.prototype=doom;
//The following invocation wont work because the function is defined on
//the prototype of MyClass. Therefore, MyClass itself
//cannot access the variables of doom directly without
//the prototype property
//MyClass.baseMethod();
//This works because instances of the class
//can access the variables defined in the
//prototype directly.
var mc = new MyClass();
mc.baseMethod();
The secret to the functionality of the prototype is intuitivley simple.
Every object in javascript has a hidden property called __proto__. Observe the 2 underscores before and after the word proto. When you create an object in javascript, even if it is an empty object, the value of the '__proto__' property is set to a default object - the Object object, that contains a number of useful methods like toString. Type the following lines in your browser console to verify this
var obj = {};
console.dir(obj.__proto__);
You find that the __proto__ references a default object with a set of functions.
Following our previous example, type the following in your browser console
console.dir(mc.__proto__);
You will find that it points to the doom object. The underlying principle is very simple. When you created an object of type MyClass, something similar to this would have executed under the hood.
mc.__proto__=Myclass.prototype;
And since you have already set MyClass.prototype to reference the doom object, you are all set to inherit the properties of the doom object.
So, whenever you create an object, imagine an invisible link from the '__proto__' of the object to the 'prototype' property of its constructor. For ordinary objects the constructor is the Object class and for your own custom classes, the constructor is the constructor function of your classes. You might want to take a look at
this article to learn more about using functions as constructors.
Another implication of this feature is that, if at any point of time, you want that all objects of your class should inherit a different set of properties, you can simply change the 'prototype' variable in the function name to a different object. That would be equivalent to dynamically swapping the a parent class at runtime, thereby enriching all the existing objects with a new set of functions, which is something is something absolutely cool because thats not something that you can do in languages like Java. What's more is that if you want to change the parent class of only one single object, you can change the '__proto__' property of that object to refer to a different object. Simple, sweet and sexy.
Thats probably all that I had to say in this post. I hope that you folks loved it.
Signing Off!
Ryan