Visit Lex

code

JavaScript’s Prototype Chain

When you first started learning to program, you may have come across the term object-oriented programming. You looked up what it meant and you found out that it’s simply a buzz word for grouping data into “objects” with attributes.

The keyword used to create these objects in many programming languages is the class. You define a class with a constructor and several public and private functions. If you want one class to inherit from another, you write simple inheritance syntax and (wala!) you have created a chain of inheritance.

Of course, this all groovy if you’re anyone but a JavaScript developer. Until ES2015, the language did not implement a class. Instead, it used and still uses a prototype chain. The new ES6 “class” is a sugary syntactic concoction that hides the inner workings of the prototype chain. Understanding how the prototype chain works is crucial if you want to develop performant code while using JavaScript’s OOP paradigm.

According to the ECMAScript standard, JavaScript objects are not class-based but are created “via constructors which create objects and then execute code that initializes all or part of them by assigning initial values to their properties. Each constructor is a function that has a property named ‘prototype’ that is used to implement prototype-based inheritance and shared properties.”

ECMAScript defines an object’s prototype as “an implicit reference to the value of its constructor’s “prototype” property.” So, a prototype chain is “a non-null implicit reference to its prototype, and so on.”

For those familiar (or not so familiar) with computer science, the prototype chain is a linked list. It’s a gross oversimplification, but keeping it simple is the key to gaining understanding. Here’s Mozilla‘s definition of a prototype chain:

When it comes to inheritance, JavaScript only has one construct: objects. Each object has a private property which holds a link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with _null_ as its prototype. By definition, _null_ has no prototype, and acts as the final link in this prototype chain.

Sounds a whole lot like a list huh? Each element in this particular list contains an object literal called a prototype.

The image above is slightly misleading because the last element in a prototype chain is always Object, from which all instances like functions and arrays derive from.

How Does Thinking of the Prototype Chain as a List Help?

When you realize that JavaScript is not inherently class-based, it changes the way you think about inheritance. For example, in other languages, sub classes only inherit the structure and behavior of a class, because classes leave state to the instance of a class. On the other hand, JavaScript objects inherit behavior, structure, and state.

When you think about prototypes as a chain it makes sense that everything becomes inheritable. From a practicale standpoint, this akes sense as well because when interfacing with the client, you want state to freely bubble up or down towawrds different sub classes. React, a JavaScript framework, takes advantage of this property by introducing components that take advantage of JavaScript’s use of classes to allow objects to inherit state.

JavaScript classes also differ from other languages because you can add properties whenever you like to objects without defining everything upfront. This allows JavaScript to be more flexible.

How Do We Initialize Our Chain?

The first thing we need to do is create a constructor. Coding by pre-ES5 standards, there is no ‘‘classy’’ way to do this. The only thing we do to differentiate a constructor function from other functions is to capitalizing the first letter. Then, we use the new keyword to create an object.

Note: the pre-ES5 example is being used for now to shun the class syntax. We’ll jump back on the ES5 wagon later.

function Bat(name){  
 this.name = name;  
}
let bob = new Bat('bob');

Every constructor we initialize gets a free prototype object as one of its properties. We can name a key and set a value to it.

Bat.prototype.fly = function(){  
  console.log('Im flying. Weeee');
 }
  
Bat.prototype.detect = function(){  
  console.log('I found a mouse!');
 }

Already, you can see the advantage of prototypical inheritance. We can add methods to our class without modifying the class definition, letting the chain handle property inheritance. This is what the chain looks like in one instance of our Bat object:

Bob{name: bob}=>Prototype{fly: [Function], detect: [Function]} => Object {} => null

Now, if we write bob.name, we get ‘bob’. And if we write bob.fly(), we get ‘Im flying. Weeee’. Hmmm. How do we have access to fly() without calling bob.prototype.fly()?

Well, it’s not magic. JavaScript’s engine first looks for the property in the object itself. If it’s not there, it traverses over to the first prototype, then the next…and the next, till it either finds what it’s looking for or hits null.

Putting it All Together

We can take advantage of prototype chains to perform class inheritance. This OOP method is also called subclassing.

function Mammal(){  
  this.bloodTemp = 'warm';  
}  
   
function Carnivore(){    
 }

function Lion(name){  
  Mammal.call(this);  //inherit constructor  
  this.name = name;  
}

We’ll create one super class and two other subclasses. Carnivore should inherit from Mammal and and Lion should inherit from both Carnivore and Mammal

Mammal.prototype.growHair = function(){  
   console.log('my hair is growing');  
}

Carnivore.prototype = Object.create(Mammal.prototype);  
Carnivore.prototype.eatMeat = function(){  
  console.log('Mmm.Meat');  
}

Lion.prototype = Object.create(Carnivore.prototype);  
Lion.prototype.pride = function(){  
  console.log('im king of the jungle');  
}

We’re back to using ES5. **Object.create()** turns a prototype into a stand alone object literal that we can then assign as the prototype of another object. This means we ignore the constructor of the super class when inheriting.

Note: That’s why we invoked Mammal.call(this) within Lion’s constructor so that we could borrow Mammal’s constructor.

Knowing how prototype chain now work, you can see how easy the jump from chaining methods to chaining objects can be.

Here’s the expected output:

var charlie = new Lion(charlie)  
charlie.growHair() // my hair is growing  
charlie.eatMeat()  // Mmm.Meat  
charlie.pride()    //im king of the jungle  
charlie.bloodTemp  // warm

Note: To achieve the same result, you can also implement mixins with Object.assign().

//classical inheritance  
Lion.prototype = Object.create(Mammal.prototype);//This is a mixin  
Object.assign(Lion.prototype, Carnivore.prototype);

Conclusion

Creating methods and inheriting using the prototype chain may seem tedious as compared to the sugary class implementation. Still, what you come to appreciate is the dynamic and modular nature of JavaScript’s language. One important thing to note is that you don’t want to get carried away with prototypical inheritance. Recall this: the charlie.growHair() function had to travel a long way up the chain before it could be executed. Short chains equal better performance.

bug_report

view_array
chevron_right