JavaScript is partly an object-oriented language.
To learn JavaScript, we got to learn the object-oriented parts of JavaScript.
In this article, we’ll look at inheritance.
Prototype Chaining
An object has a prototype chain.
The inherit properties from them.
For instance, we can create a chain of constructors by writing:
function Shape() {}
Shape.prototype.name = 'Shape';
Shape.prototype.toString = function() {
return this.name;
};
function Square() {}
Square.prototype = new Shape();
Square.prototype.constructor = Square;
Square.prototype.name = 'Square';
We have the Shape
constructor with some prototype properties.
Then we created a Square
constructor with the prototype
set to the Shape
constructor.
Then we set the constructor
to the Square
to make instanceof sqaure
returns true
if we use instanceof
with a Square
instance.
If we want to call the parent constructor within the child constructor to populate its properties, we can change the code.
We can write:
function Shape(name) {
this.name = name;
}
Shape.prototype.toString = function() {
return this.name;
};
function Square(name, length) {
Shape.call(this, name);
this.length = length
}
Square.prototype = Object.create(Shape.prototype);
Square.prototype.constructor = Square;
to create the Shape
constructor with the name
property.
Then we have the Square
property with the name
and length
parameters.
We call the Shape
constructor with call
and we set the first argument to this
so it’s called with the Square
constructor as this
.
name
is what we pass into the Shape
constructor.
To create the prototype, we call Object.create
to inherit the properties from Shape.prototype
.
And we set the constructor
the same way so that instanceof
still reports correctly.
Class Syntax
We can make this easier with the class syntax.
With it, we can rewrite:
function Shape(name) {
this.name = name;
}
Shape.prototype.toString = function() {
return this.name;
};
function Square(name, length) {
Shape.call(this, name);
this.length = length
}
Square.prototype = Object.create(Shape.prototype);
Square.prototype.constructor = Square;
to:
class Shape {
constructor(name) {
this.name = name;
}
toString() {
return this.name;
};
}
class Square extends Shape {
constructor(name, length) {
super(name);
this.length = length
}
}
We use the extends
keyword to inherit the properties from Shape
‘s properties in the Square
class.
super(name)
is the same as Shape.call(this, name);
.
Prototype
We can get the prototypes in the chain with the __proto__
property.
For instance, we can write:
console.log(square.__proto__);
then we get the Shape
constructor.
We can get the __proto__
of the __proto__
property, so we can get:
console.log(square.__proto__.__proto__);
then we get the Shape.prototype
object.
Then if we call it again:
console.log(square.__proto__.__proto__.__proto___);
We get the Object.prototype
object.
We can confirm this by writing:
console.log(square.__proto__ === Square.prototype);
console.log(square.__proto__.__proto__ === Shape.prototype);
console.log(square.__proto__.__proto__.__proto__ === Object.prototype);
Then they all log true
.
Objects Inherit from Objects
Objects can inherit from other objects directly.
To do that, we use the Object.create
method to return an object that returns an object with a given prototype object.
For instance, we can write:
const proto = {
foo: 1
};
const obj = Object.create(proto);
Then we can check the prototype of obj
with the __proto__
property:
console.log(obj.__proto__ === proto);
then that logs true
.
Conclusion
We can create objects that inherit from other objects.
Also, we can check the prototype chain of an object to see what’s inherited from.
Then class syntax makes inheritance with constructors much more easily.