JavaScript: prototype inheritance

You may be familiar with JavaScript not being a pure object-oriented language. If you aren’t already familiar with this fact, well, sorry: it isn’t. Not strictly, anyhow. JavaScript is a prototype-based language. However, this is probably not the first time a JavaScript programmer has heard the term. In fact, you may know it from something like this:

String.prototype.reverse = function(){
return [...this].reverse().join("");
}

This is an example of extending a class’s prototype. Now, I am hesitant to use the word “class”, since it is misleading. Unlike the canonical class, JavaScript only simulates a class using its model of prototypes, and thus its class statement is only syntactic sugar. So, the following ES6 code:

class Vector2D {
constructor(x, y){
this.x = x;
this.y = y;
}

add(ent){
if(Vector2D.isVector(ent)){
return new Vector2D(this.x + ent.x, this.y + ent.y);
} else {
return new Vector2D(this.x + ent, this.y + ent);
}
}

static isVector(vec){
return vec instanceof Vector2D;
}
}

is roughly equivalent to:

function Vector2D(x, y){
this.x = x;
this.y = y;
}
Vector2D.prototype.add = function(ent){
if(Vector2D.isVector(ent)){
return new Vector2D(this.x + ent.x, this.y + ent.y);
} else {
return new Vector2D(this.x + ent, this.y + ent);
}
}

Vector2D.isVector = function(vec){
return vec instanceof Vector2D;
}

In reality, there are some checks, such as those made to ensure that the constructor is called with the “new” keyword. Oh, and classes cannot be hoisted! Anyhow, this approximation will suffice.

The prototype itself is used to contain functions that are “endowed” to instances of the class. That is, each property of the prototype is given as properties to the instance. So, when calling “new Vector2D(3, 4)”, the “add” member of the prototype is assigned to the Vector2D instance. Let’s use a different class to illustrate this:

class Car {
constructor(name, year){
this.name = name;
this.year = year;
}

info(){
return "This car is a " + this.year + " " + this.name + "!";
}
}
let myCar = new Car("land cruiser", 2003);
myCar.info(); // "This care is a 2003 land cruiser!"

This is roughly equivalent to:

let myCar = { name: "land cruiser", year: 2003 };
myCar.info = (function info(){
return "This car is a " + this.year + " " + this.name + "!";
}).bind(myCar);
myCar.info(); // "This care is a 2003 land cruiser!"

This code snippet simulates the process of prototype endowing. This also highlights the “bind” property of functions. It’s purpose is simple: it updates the value of “this” inside the function to its argument. So, whenever “this” is mentioned in “myCar.info”, it’s referring to “myCar” because of the bind statement on the function.

You may ask yourself, “But isn’t this pointless? Why not refer to ‘myCar’ directly?” Well, mainly since the purpose of a class is to provide a template for its instances, and thus, it is useful to refer to the general “instance” as “this”, since you cannot name it directly.

Now, one cool thing about “Function#bind” and prototypes is that the members of a prototype can be used as members of other prototypes! In fact, so long as a class or object implements all necessary functions referenced in the desired prototype, it can utilize it! For example, let’s consider the “Car” class from earlier. Observe that, in the following code, we can use the prototype function with bind on objects and in other class’s prototypes.

class Car {
constructor(name, year){
this.name = name;
this.year = year;
}

info(){
return "This car is a " + this.year + " " + this.name + "!";
}
}
let myCar = new Car("cruz", 2015);
myCar.info(); // "This care is a 2015 land cruiser!"
let poserCar = {
name: "chariot",
year: "23 B.C."
};
poserCar.info = Car.prototype.info.bind(poserCar);
poserCar.info(); // "This car is a 23 B.C. chariot!"
class ForeignCar {
constructor(name, year, country){
this.name = name;
this.year = year;
this.country = country;
}
}
ForeignCar.prototype.info = Car.prototype.info;
let theirCar = new ForeignCar("small-car", 2018, "antarctica");
theirCar.info(); // "This car is a 2018 small-car!"

Conclusion

Prototypes are the way JavaScript pulls of “object-oriented”. Prototypes, however, are just objects (with function values) with a special property of automatic endowment. These prototypes are attached to objects that can be called as constructors with the “new” keyword, and are essentially equivalent to “bind”ing the function to the instance.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.