JavaScript doesn’t follow a classical inheritance model (although it can be made to do so), but instead uses a prototypal inheritance model at its core. See my introduction post on prototypes and objects if your not clear how this works.
What this means is that instead of having classes inheriting from other classes you have simply objects inheriting from objects via an internal link called its prototype. A “class” in JavaScript is typically nothing more than a function.
Let’s pretend we have a game, with a base Actor class that provides properties used by the different types of objects in the game. We’ll also have a Player class and a Monster class that inherit from that and add their own properties.
In that setup, the Actor and Player classes might look like this:
// Our base class
function Actor() {
this.positionX = 0;
this.positionY = 0;
}
// Define a method on Actors prototype so it can be inherited
Actor.prototype.move = function(x, y) {
this.positionX = x;
this.positionY = y;
}
// Create a new player class that will inherit from Actor
function Player() {
Actor.call(this); // Calls Actor, creating local instances of its properties.
this.health = 100; // Add a unique var on Player instance.
}
At this point, if we create a new player by calling new Player
, it will have
3 variables defined on it. The two position variables from Actor and one
defined by Player, But if you try to call player.move(x, y), JavaScript will
give you an error saying that it’s not a function. It’s missing from the Player
instance.
In order for our players to inherit the move
method, our prototype chain needs
to have Player
inheriting from Actor
.
Our first attempt to do this might be to try to set the prototype directly:
// This will not work right.
Player.prototype = Actor.prototype;
However, this would mean that anything you do on the Player prototype would would affect the Actor prototype (because it’s a reference to the same object). If you define a method on Player.prototype, it will actually be created on the Actor.prototype, which will affect all other things that inherit from Actor.
Our intent is to have Player inherit from Actor, while still maintaining its own
prototype that we can make changes to and that its children could inherit from.
To do this, Player
must have its own prototype while its prototype’s prototype
is Actor. We might try doing it like this:
Player.prototype.prototype = Actor.prototype; // Won't work
The problem with that is we can only affect an object’s direct prototype.
Because we can’t modify the prototype’s prototype we need to create an inheritance
chain using the new
operator.
Player.prototype = new Actor();
This is creating a new instance of Actor, whose prototype is Actor’s
prototype. It has everything that Actor has, but modifying it will modify
the instance, and not Actor’s prototype. This is good, but the problem is that
this expression calls Actor’s constructor
. The constructor
is nothing more
than the part of the “class” function that is executed when we create a new
instance of it.
If your constructor has side effects, such as modifying an external state, you don’t want this to occur at the time you’re creating your new Player class – only at the time you’re creating new instances of Player.
For example, let’s say that the Actor constructor logged stats about how many actor instances were created:
var stats = {numActorsCreated: 0};
function Actor() {
stats.numActorsCreated++; // I'm going to run when a new instance is created.
this.x = 0;
this.y = 0;
}
function Player() {
Actor.call(this);
this.health = 100;
}
Player.prototype = new Actor();
var p = new Player();
console.log(stats.numActorsCreated); // 2
Because we created a new Player prototype using new Actor()
, we have
prematurely incremented our stat before any actor instances were actually
created.
We can workaround this by creating a temporary empty constructor function
that we can use with new
. We can set its prototype to Actor’s prototype, and
use a new instance of it as Player’s prototype:
var stats = {numActorsCreated: 0};
function Actor() {
stats.numActorsCreated++;
this.x = 0;
this.y = 0;
}
function Player() {
Actor.call(this);
this.health = 100;
}
function Temp() {};
Temp.prototype = Actor.prototype;
Player.prototype = new Temp(); // No side effects
var p = new Player(); // calls our Actor constructor
console.log(stats.numActorsCreated); // 1
We now have a prototype chain that links our new player p
to Player, and then
to Actor. That works, but if we want to repeat this for other classes it could
get pretty verbose. It might be easier to create a reusable function:
function inherit(parent) {
function Temp() {}
Temp.prototype = parent.prototype;
return new Temp();
}
function Actor() {
this.x = 0;
this.y = 0;
}
function Player() {
Actor.call(this);
this.health = 100;
}
Player.prototype = inherit(Actor);
function Monster() {
Actor.call(this);
this.health = 10;
}
Monster.prototype = inherit(Actor);
That’s a simple example of using prototype for classical inheritance in
JavaScript. Thanks to additions to the JavaScript
language like Object.create()
, inheritance has gotten even easier.
If you want to learn more about how that can simplify your code, take a look at my post on inheritance using Object.create().