JavaScript Prototypal Inheritance Explained

E.Y.
4 min readDec 1, 2020

--

Photo by Frédéric Perez on Unsplash

When I first switched from Ruby to JavaScript, I found it challenging to grasp the concept of object prototype and the prototypal chain. The class keyword introduced in ES2015 is simply a syntactical sugar and does not make it easier to understand.

Yet regardless prototype-based or class-based, the reason to have chain or method resolution order (in Python) is to establish connections among classes and better use the inheritance relationship.

When it comes to inheritance, JavaScript only has one construct: objects. Each object has a private property called prototype object, which acts as a template object that it inherits methods and properties from another object (like the __mro__ in Python). 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. Before null is reached the second to last stop should be Object, as nearly all objects in JavaScript are instances of Object .

How is this working? Well, let’s look at an example.

let f = function () {
this.a = 1;
this.b = 2;
}
let o = new f(); // {a: 1, b: 2}
f.prototype.b = 3;
f.prototype.c = 4;

Right now:

o.[[Prototype]] has properties b and c, and

o.[[Prototype]].[[Prototype]]is Object.prototype, and

o.[[Prototype]].[[Prototype]].[[Prototype]] is null.

So the whole property chain looks like:

{a: 1, b: 2} ---> {b: 3, c: 4} ---> Object.prototype ---> null

Note that {a: 1, b: 2} are object f’s own properties that are not inherited from anywhere else.

When we try to access o.b we get value 2 . Why not 3 ?Because .b exists as f own property, and if it is defined, then the value is read and the prototype chain walk stops here. Even the prototype also has a b property, but it's not visited. This is called Property Shadowing.

Let’s look at a more concrete example on walking up the chain of prototypes.

function doSomething(){}
doSomething.prototype.foo = "bar";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log(doSomeInstancing.prop); //some value
console.log(doSomeInstancing.foo); // bar
console.log(doSomething.prop); // undefined
console.log(doSomething.foo); // undefined
console.log(doSomething.prototype.prop);// undefined
console.log(doSomething.prototype.foo); // bar

As seen above, the __proto__ of doSomeInstancing is doSomething.prototype.

When you access a property of doSomeInstancing, the browser first looks to see if doSomeInstancing has that property.

If doSomeInstancing does not have the property, then the browser looks for the property in the __proto__ of doSomeInstancing (a.k.a. doSomething.prototype).

If the __proto__ of doSomeInstancing does not have the property, then the browser looks for __proto__ of the __proto__ of doSomeInstancing, in this case window.Object.prototype.

If the property is not found, then the __proto__ of the __proto__ of the __proto__ of doSomeInstancing is looked through, in which case null.

But __proto__ does not exist on null. So the result undefined is returned.

In short, prototype is a property of a Function object. It is the prototype of objects constructed by that function.

__proto__ is internal property of an object, pointing to its prototype.

function example(x, y) {
this.x = x;
this.y = y;
}

When JavaScript executes this code, it adds prototype property to example, prototype property is an object with two properties to it:

constructor
__proto__

So when we do example.prototype it returns

constructor: example(x,y)
__proto__: Object

Now as you can see constructor is nothing but the function example itself and __proto__ points to the root level Object of JavaScript.

var myExample = new example();

In above line we create an instance of example . How it works?

  • It first creates an empty new object {}
  • It creates __proto__ on myExample and makes it point to example.prototype so myExample.__proto__ === example.prototype
  • It executes example.prototype.constructor (which is definition of function example ) with the newly created empty object as its context (this), so the x,y property gets added to newly created object.
  • It returns newly created object
// the following are all true
myExample.__proto__ == example.prototype
myExample.__proto__.__proto__ == Object.prototype
myExample.__proto__.__proto__.__proto__ == null
myExample instanceof example;
myExample instanceof Object;

There are different ways to create create objects and the resulting prototype chain.

  • Objects created with syntax constructs
var o = {a: 1};
  • With a constructor

A “constructor” in JavaScript is “just” a function that happens to be called with the new operator.

function Rectangle() {
this.length = 2;
this.width = 3;
}
Rectangle.prototype = {
area: function() {
this.length * this.width;
}
};
var re = new Rectangle();
// re is an object with own properties 'length' and 'width'.
// re.[[Prototype]] is the value of Rectangle.prototype when new Rectangle() is executed.
  • With Object.create

ECMAScript 5 introduced a new method: Object.create(). Calling this method creates a new object. The prototype of this object is the first argument of the function:

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (inherited)
  • With the class keyword

ECMAScript 2015 introduced a new set of keywords implementing classes. The new keywords include class, constructor, static, extends, and super.

Be careful with accessing or modifying prototype.

Note that the __proto__ is considered depreciated. You should use the following methods if possible.

  • Object.create(proto, [descriptors]) — creates an empty object with given proto as [[Prototype]] and optional property descriptors.
  • Object.getPrototypeOf(obj) — returns the [[Prototype]] of obj.
  • Object.setPrototypeOf(obj, proto) — sets the [[Prototype]] of obj to proto.

But don’t modify the prototype of an object on the fly if possible as it should only be generated when the object is created. JavaScript engines are highly optimised for this. Changing the prototype in the later stage will break the internal optimisations for object property access operations.

That’s pretty much of it today!

Happy Reading!

--

--

No responses yet