Demystifying JavaScript’s Prototype Inheritance

At some point, every student learning JavaScript has heard that JavaScript doesn’t have classes. Or perhaps they heard that JavaScript…

At some point, every student learning JavaScript has heard that JavaScript doesn’t have classes. Or perhaps they heard that JavaScript doesn’t have ‘real’ classes.

Yet if you look at the code below, or open up your browsers console and type ‘class’ you’ll clearly see some syntax highlighting. JavaScript clearly seems to have a class, and what exactly is a ‘real’ class anyway?

Does JavaScript have classes? Well, yes, in the sense that the keyword does in fact exist, and it does do something. It, however, does not mean the same thing as the class keyword in languages like Java. Its only purpose is to be syntactic sugar for creating a constructor function and setting its prototype.

A lot of fancy words there, so let’s break that down. When we say that something is syntactic sugar, we mean that it serves as a nicer, cleaner, possibly easier to read way of doing something.

Syntactic sugar isn’t a feature! It doesn’t add any additional functionality to the language that didn’t already exist before. If JavaScript classes are syntactic sugar, it means that there was already an existing way to write the code above. In fact, there was, although it didn’t look quite as nice.

Here, on the first line, we have JavaScript’s plain old function keyword. We’re taking in two arguments and using the this keyword to dynamically set the value of name and age.

This works because of the code on line ten, where we actually tell JavaScript to create an instance of the PersonOldWay function.

Line six is where we give this PersonOldWay constructor function the sayHello function. Notice that, in order to do so, we access the PersonOldWay prototype property.

Under the hood, we are actually doing the exact same thing in the first code snippet. Remember that the first code snippet is just a prettier version of the second code snippet. It doesn’t actually achieve anything that couldn’t be done before.

Inside of our Person.prototype, we see both the constructor function and the sayHello function that we defined. The prototype property will continue to expand as we add more and more methods to our class, but you also likely noticed that __proto__ points to Object. What exactly is that?

__proto__ tells us that Person inherits from Object.prototype. If we look at Object.prototype, we can see various methods defined there — the toString method for example. We can call the toString method on the Person class, because Person inherits these methods from further up the prototype chain.

This is a good thing, as it allows us to be more memory efficient by simply writing a method once, and then letting objects that need it, inherit it.

When running a piece of code, JavaScript will first check to see if the object in question has that property. If it does, it questions whether it will use it. Otherwise, it will look at that object’s __proto__, in our case Person.prototype, to see if the property exists there.

It will repeat this process until it either finds it, or it reaches the end of the chain and returns null.

If you are familiar with a language that uses classical inheritance (like Java or Ruby), some of this may sound familiar. Languages that use classical inheritance also have a way of reusing code, so what exactly is the difference?

In JavasScript, everything is just an object inheriting from another object. There are no blueprints, but rather prototypes. A prototype itself is a working piece of code. I can give functionality directly to the Person function. I can do this at any point after defining the function. What exactly do I mean by a working piece of code?

Here, a new function call personFunction was make directly on Person. It doesn’t do anything special; it just returns a string. The object adam inherits from Person, which is to say that adam.__proto = Person.prototype.

Notice though, that we cannot simply call adam.personFunction(), because that function does not exist on the adam inheritance chain. Rather, it exists directly on the constructor. Person can do more than just define how adam will behave, meaning it can do work.

Above, we define a simple Java class called Dog and give it a three-argument constructor. This class will define how each instance of Dog will look. Every instance of Dog will have two strings corresponding to name and breed, and one integer corresponding to its age. The class itself does not do any work.

In classical inheritance languages, the class functions as a blueprint for creating objects (instances of the class). The class itself serves no purpose, other than to define the methods and attributes that its instances will inherit.

In other words, you can’t do anything practical with a Java class, other than define how its instances will behave. Classes in Java will inherit from other classes, provided the child has a potentially larger, more robust blueprint.

However, it doesn’t matter how long the inheritance chain is, the class itself will never be more than just a blueprint.

Sources