Prototype inheritance in JavaScript
JavaScript has its own inheritance model that is quite unique among languages.
Since JavaScript has no notion of classes yet and since everything is actually
an object, we will try to clear the confusion by defining a few terms:
- A class is a constructor function which has properties and other functions defined on it.
- An instance is an object created with
new Class
.
Instead of inheriting from a base class, instances in JavaScript
inherit from a prototype object.
The prototype is then referenced by every new instance of the object.
An instance can then have its own properties and methods beyond those
inherited from the prototype.
JavaScript uses prototype chains: object instance C can have as prototype
object instance B which in turn can have as prototype object instance A.
A property lookup traverses the prototype chain upwards.
Uses
So what are prototypes useful for?
Subclassing
The classical inheritance model found in other languages can deal with subclassing very easily. However, we can achieve subclassing in JavaScript using the prototype.
Suppose we have a parent class with a few methods declared on its prototype.
function ParentClass() {}
ParentClass.prototype = {
firstMethod: ...
secondMethod: ...
};
We can then create a child class which has as its prototype set to an instance of the parent class.
function ChildClass() {}
ChildClass.prototype = new ParentClass();
If we create a new instance of each of these two classes we will notice that the child instance inherits from the parent class.
parentInstance = new ParentClass();
childInstance = new ChildClass();
> childInstance instanceof ParentClass
true
We achieved this by chaining the two classes’ prototypes. A simplified schema of the inheritance would be:
child class → child.prototype → parent class → parent.prototype
Typed Errors
Using the same pattern we can create typed errors. Errors are JavaScript’s equivalent to Exceptions found in other languages. First we need to define a constructor function.
function MyError(message) {
this.name = 'MyError';
this.message = message;
}
Then assign an Error instance to its prototype.
MyError.prototype = new Error();
And finally we can use it.
throw new MyError('something happened');
A nice feature of typed errors is that we can handle each error type
using instanceof
. A lot like the exception handling from other languages.
Only handle MyErrors and rethrow the others.
try {
throw new MyError('something happened');
} catch (e) {
if(e instanceof MyError) {
...
} else {
throw e;
}
}