Prototype and Inheritance in JavaScript

In brushing up on my foundational JavaScript knowledge, I realized that I had never done a deep-dive on prototype. In the early days of my learning JavaScript, I had come across the __proto__ object property in the console. I clicked on it, saw a whole bunch of properties that I was not immediately concerned with, and made a mental note to someday investigate. Today is that day, and it turns out that I really should have investigated sooner as prototype is key to understanding how object properties are passed down to other instances of that object. Having learned JavaScript after becoming comfortable with the class-based nature of Ruby, an understanding of prototype would have cleared up some confusion around the differences between inheritance in the two languages.

While ES6 saw the introduction of the class keyword in an attempt to make JavaScript appear more object-oriented, it is merely syntactic sugar. JavaScript at its heart is a prototype-based language. In my experience, I think this made it easy to take this syntactic sugar at face value, draw parallels to Ruby classes, and not delve deeper into the differences. And while this may make JavaScript more accessible to those familiar with class-based languages, it may mask the true nature of how inheritance in JavaScript occurs.

Introduction to Inheritance in JavaScript

When we discuss inheritance in JavaScript we are talking about objects. Each object has a private property that connects it to its prototype (another object). Each prototype has its own prototype and the chain continues until we come to an object containing null as its prototype. As null represents nothing, it has no prototype and is the end of the prototype chain. All objects created in JavaScript are themselves instance of Object and therefore have their own prototype chains descending from Object.

Inheriting Properties with Prototype

JavaScript objects are dynamic and include a set of properties (termed own properties). They also are linked to a prototype object, which is linked to a prototype object and so on. When we attempt to access an object’s property, the property will be searched for on the object, then the prototype of the object and all the way along the prototype chain until the property is found or we reach null on the chain. To illustrate:

let f = function() {
    this.a = 1;
    this.b = 2;
}
let o = new f();
f.prototype.b = 3;
f.prototype.c = 4;
console.log(o.a); // 1
console.log(o.b); // 2
console.log(o.c); // 4
console.log(o.d); // undefined

To begin, we create a function, f, with properties of a=1 and b=2. We then create an object, o, from f. Next, we add properties to the prototype of f, creating a new property c=4 and updating b to equal 3. When we log the properties of our object, o, we are presented with the commented out results. The property of a is 1, as expected. The property of b is 2 because we look first on o to see if there is a b property. There is and its value is 2. We therefore never make it up the prototype chain to f where the value of b is 3. This is known as Property Shadowing. For the case of c, we look to o for a c property and do not find one, leading us up to f where the property of c is found to equal 4. Finally, we are searching for the property of d which is not a property of o and neither is it a property of f. We go all the way along the prototype chain to null without finding d and thus undefined is the result.

#inheritance #web-development #prototype #javascript

Prototype and Inheritance in JavaScript