Skip to content

Instantly share code, notes, and snippets.

@amerrika
Last active April 18, 2024 14:36
Show Gist options
  • Save amerrika/34e9f99b4d6a0c37572e9644b6a5e48b to your computer and use it in GitHub Desktop.
Save amerrika/34e9f99b4d6a0c37572e9644b6a5e48b to your computer and use it in GitHub Desktop.

Inheritance

After grasping the concept of prototype objects in the first article (Prototypes) , we are going to focus on the inheritance. Without going too deep and covering all aspects this article should answer following questions:

  • How inheritance works
  • Why it is an important concept in JavaScript and
  • Why it is useful for us to understand it

Prototypal Inheritance

This kind of inheritance is based on the prototype chain. A prototype object is just that: a regular object. This is way it's necessary to have some kind of mechanism that makes it possible for created objects to inherit methods and properties. We have already mentioned this mechanism briefly in our Prototypes article. It is generally called internal prototype (historically _proto_ but the use of it has been deprecated).

How can this concept be helpful in JavaScript? Say, we have three objects A,B,C connected by prototypal inheritance. In case that object B has a property that object A needs but doesn't have it, object A can access this property as if it was its own. The same applies if object B needs a property that object C has, but B doesn't. Equally the object A can have access to properties of object C. Here we can see the importance of this concept, it makes possible for code to be shared!

Let's Start with Examples

To implement such kind of inheritance hierarchy we are going to create three constructor functions:

function Device() {
  this.category = "Device";
  this.subcategory = "Electronics";
  this.getPath = function () {
    return this.category + "/" + this.subcategory;
  };
}

function Usb() {
  this.name = "USB Memory Stick";
}

function Mouse(connectivity, dpi) {
  this.name = "Computer Mouse";
  this.connectivity = connectivity;
  this.dpi = dpi;
  this.getMouseInfo = function () {
    return `A ${this.connectivity} ${this.name} with maximum DPI of ${this.dpi} `;
  };
}

A code where this inheritance "magic" happens is following:

Usb.prototype = new Device();
Mouse.prototype = new Usb();

What happened here? We swapped the inital prototype object of the Usb() constructor with an instance of Device() constructor. Then we did the same with the prototype of Mouse() which we replaced with an instance of Usb() constructor.

console.log(Usb.prototype)

img-1

What we see in the image above is that prototype object of Usb() constructor now contains all direct properties of the Device() instance (category, subcategory and getPath). We can also recognize that internal <prototype> object points to the prototype object of Device() constructor which is empty.

console.log(Mouse.prototype)

img-1 1

As for prototype object of Mouse() constructor, we can see that it contains a direct property of the Usb() instance (name). The internal <prototype> points to the prototype object of the Usb() constructor.

These properties and methods are not inherited from Device() constructor but from its instance. We can now modify or even delete the Device() constructor which will have no effect on the Usb(). All we need is one instance from which features will be inherited.

Side Effect of Replacing Prototype Object

When we replace prototype object, instead of just adding methods and properties to it, we get side effect for the constructor!

console.log(Usb.prototype.constructor); // Device()
console.log(Mouse.prototype.constructor); // Device()

After inheritance process it's a good idea to again set constructor to default:

Usb.prototype.constructor = Usb;
Mouse.prototype.constructor = Mouse;

console.log(Usb.prototype.constructor); // Usb()
console.log(Mouse.prototype.constructor); // Mouse()

Let's Analyze the Inheritance Process

We're going to create an instance of Mouse() and call the getMouseInfo() method.

const mouseOne = new Mouse("wired", 4800)
console.log(mouseOne.getMouseInfo()) // A wired Computer Mouse with maximum DPI of 4800

This was expected behaviour as the getMouseInfo() is direct method of the mouseOne object. Now, the mouseOne object doesn't have its own getPath() method but it will inherit it and use it as its own.

console.log(mouseOne.getPath()) // Device/Electronics

When calling mouseOne.getPath() JavaScript Engine does the following:

  • looks up own properties of mouseOne and doesn't find the getPath() there
  • identifies the internal <prototype> of the mouseOne (because this refers to the mouseOne) img-2
  • this internal <prototype> points directly to the prototype object of its Mouse() constructor but JavaScript doesn't find the method there either and thus it has to keep looking.
  • the prototype object of the Mouse() constructor also contains internal <prototype> object. Now, this <prototype> points to the prototype object of the Usb() constructor. img-3
  • Finally, JavaScript Engine finds the getPath() method and this method is called as if it was own method of mouseOne.

Follow Internal Prototypes up to the Object()

Now, because every object in JavaScript has internal <prototype> by following these internal <prototype> objects we can reach the prototype object of the Object() constructor. img-4

Why to put your time and effort in understanding this mechanism?

This inheritance mechanism is not easy to understand. For the ones who are just starting learning JavaScript I would say it's not that important. On the other hand for the intermediate learners and the ones using frameworks/libraries I'd say it's important to understand it. From my point of view, the reasons are following:

  • Whenever we use Inspect tool to open Console we'll see all this <prototype> objects. It helps to understand their purpose in order not to get confused by them.
  • The same applies when working with frameworks because prototyple inheritance is widely used in JavaScript frameworks and libraries and it's useful to know how all these different methods and properties are inherited.

Resources

  • Object Oriented JavaScript - third edition by Ved Antani and Stoyan Stefanov

Disclaimer

By no means am I a JavaScript expert, so feel free to comment and point to the potential mistakes. I write articles as my learning experience and with hope it can be useful for others.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment