DEV Community

loading...
Cover image for JavaScript Inside Story : More about Prototypes and Inheritance

JavaScript Inside Story : More about Prototypes and Inheritance

mayankav
web enthusiast ;)
・8 min read

I almost picked the title "JavaScript's Factory for Everything", but then I changed my mind after reading my older post. Just trying to push my last post here. I don't feel very comfortable writing this, not because I don't understand the stuff but because its something that pretends to be something it really is not. Protesting that prototypal inheritance is not inheritance at all does not change anything. Perhaps if it were not for the comfort of OOPers, it could have been better called prototypal wiring or maybe prototypal delegation. If you're coming from Java, how would you answer the question, "Why does Java not suppport multiple class inheritance?". Well, you'd point out the Diamond Problem, won't you? Given that JavaScript doesn't know this problem how would you explain it not being able to support such a thing? PS: It can be done but it has its own issues. Despite its appearance resembling to that of classical inheritance, JavaScript has its own reasons for (not) being able to do certain things. Not asking you to unlearn the classical inheritance take but then to understand JavaScript's way of handling prototypal inheritance you need to drop those intuitive assumptions atleast for the time being. ‍‍

Just forget it



I dont want to recall the time when I started logging objects in the console only to check what's really inside of them. I mean I knew objects had their properties but I also found stuff like __proto__, constructor, prototype, __proto__ again. It would not stop. I kept on digging and it kept on throwing more and then I realized I was willingly stuck in a loop of references. Let me take help of an analogy to explain this to you. Lets assume that any function you create in JavaScript is a House to be put on rent. The House then comes with a bunch of keys ( prototype ). The bunch of keys has another bunch of small master keys ( __proto__ ) inside it and has a label ( constructor ) with the name of the house on it. This bunch of keys is given to the tenants ( objects created from the function ) and the tenants then keep it with them and they like to call it guess what ? __proto__. Heck! confusing. Its not that easy to build analogies here. Just take a look at the diagram I came up with.

Prototypes in JavaScript

Check out the Ppt

What you see is what I am going to pen down now. Consider a class A or simply a function A. The function when created, gets two properties by default namely prototype and __proto__. __proto__ is a property that is available on everything in JS be it a primitive or an object. Go ahead! try that in your console. It contains some information from the function that was responsible for creating the primitive/object in the first place. Since a function is nothing more than a Function object, the __proto__ property on each and every function takes its value from Function.prototype. What about the prototype property? In contrast with __proto__ which is available on anything and everything in JS, prototype is only available on JavaScript functions. The prototype property is an object ( only exception being Function.prototype which is a native function ) with two default properties namely constructor ( refers to the Function/Class itself to which prototype belongs ) and __proto__ . The purpose of __proto__ on A.prototype is nothing different from that on the function A itself. A.prototype.__proto__ contains information about the function that was responsible for the creation of A.prototype. Since this object (A.prototype) was automatically created, the function/class responsible for its creation was Object. No wonder why every SomeFunction.prototype.__proto__ gets a default value of Object.prototype. To verify this, try creating an object using the object lietral syntax like so. ‍

let randomObj = {};
console.log(randomObj.__proto__ === Object.prototype); // true
Enter fullscreen mode Exit fullscreen mode
Try on Codepen




Moving on to instantiating the constructor function, lets do let objA = new A(); to create an object from the function A. objA gets a __proto__ . We just discussed how everything in JS gets this default property with a value of SomeFunction.prototype, SomeFunction being the function/class responsible for its creation. No points for guessing its value in this case. Its A.prototype. ‍
let objA = new A();
console.log(objA.__proto__ === A.prototype); // true 
console.log(objA.prototype); // undefined
Enter fullscreen mode Exit fullscreen mode
.prototype only exists on functions. Try on Codepen.


Prototypal Inheritance‍

All this while what I've been trying to tell you is that __proto__ is just the pen name of prototype. A constructor's prototype becomes it's object's __proto__ . How does this help? Well since its not the copy but the reference to a function's prototype which is shared amongst objects created using the function, any new function/property on the prototype of a function is readily available on the object's __proto__ as well. Though its not a good practice to monkey patch properties on the protoype of a constructor function. Read more about that here. Interestingly you dont even need to access the patched property via __proto__ . You just access it on the object like objA.somePatchedFunction() and it gets resolved from __proto__ in the chain. This sounds fun but can very quickly get on the nerves when some object starts patching functions/properties on its __proto__ property causing a prototype namespace pollution.

Anyway, did you think of this? What happens when you manually alter the __proto__ property on objA like objA.__proto__ = { random : 10 } ? Apparently, the link from the function A breaks and you can no more access the functions patched on A.prototype and then you get access to properties of the newly set object ({ random : 10 }) like objA.random. Apart of assigning a value directly to objA.__proto__ there exist lawful functions (Object.setPrototypeOf, Object.create) in JavaScript to help you do this some of which are on the verge of being deprecated but that's not my concern for this blog post. This does not even seem very helpful. Why don't we try altering the __proto__ property on A.prototype? Well, that sounds like a plan.

function Parent() {
  this.p = 50;
}
Parent.prototype.patchedP = 100;

function Child() {
  Parent.call(this);
  this.c = 200;
}

//  Object.create sets (Child.prototype).__proto__ = Parent.prototype
Child.prototype = Object.create(Parent.prototype);
// Resetting the child constructor may/may not be needed
Child.prototype.constructor = Child;

Child.prototype.patchedC = 400;
console.log(new Child().p); // 50  //undefined if no Parent.call(this)
console.log(new Child().patchedP); //100
Enter fullscreen mode Exit fullscreen mode
Try on Codepen

For the ease of understanding lets try to alter Child.prototype. Let me tell you what am I trying to do. When I create a new object using the Child constructor function, I can say something like new Child().c and get the expected value i.e. 200. What I want is to get a valid value on doing so new Child().patchedP ie. 100. Do you think I should simply make an assignment like so Child.prototype = Parent.prototype ? Well , no because then when you want to patch some functions on Child.prototype, you will end up patching the orignal Parent.prototype. Changes on Child should not impact Parent or you can't call it inheritance. I better use an intermediary object to set the prototype of child. That's why we do this Child.prototype = Object.create(Parent.prototype). Now when you patch the Child's prototype, it won't impact the Parent (you only patch the intermediary object).

Did you notice the call to Parent function within the Child function (kind of super if you're coming from Java) ? Try commenting it out in the pen. That shall leave you with no access to Parent's instance properties i.e. p here. When you call the Parent with 'this' (this refers to the newly created object when you say new Child()), the Parent function executes to add the property p on new Child(). Now in every new instance you create from the Child function, you get access to instance properties of Parent & Child both along with patched properties of both Parent.prototype & Child.prototype. Additionally, now patching Child.prototype will not impact Parent. Now that's something we can kind of call inheritance. Just to touch upon the concept of prototype chaining, it goes without saying that if you try to access aChild.randomProperty given aChild = new Child(); , it shall be first looked up in the property list of aChild itself, if not found it should be searched for in aChild.__proto__ (the intermediary object we discussed earlier), next in aChild.__proto__.__proto__ until the search reaches Object.prototype which is the last man standing in the prototype chain.

A look at new Child()‍

A look at new Child()‍


Take Away

  1. Every function's .prototype property is of type - object except function Function. (It's .prototype property is of type - function)

  2. Every function's .__proto__ property is always equal to Function.prototype and hence of the type - Function.

  3. Objects don't have .prototype property.

  4. Every object's .__proto__ property is of type object.

  5. An object's .__proto__ property takes its value from the .prototype property of the Function from which it was created.

  6. If an object was not created using any particular function (created using object literal or using Object.create(Object.prototype)), the value of it's .__proto__ property will be Object.prototype.

  7. Create an object from a class A or a function A : let objA = Object.create(A.prototype); or let objA = new A();

  8. In ES5, inheritance looks like so : let anObjectFromParent = Object.create(Parent.prototype); Child.prototype = anObjectFromParent;

  9. In ES6, the extends keyword plays the role of Object.create(Parent.prototype) and the super keyword invokes the constructor of the parent.‍‍

  10. Accessing __proto__ directly on an object is not as optimal as using the new keyword, Object.create (to set) and Object.getPrototypeOf (to get).

  11. __proto__ is just a way to programmatically access an object's [[Prototype]] internal slot which is otherwise not accessible in code.

Originally Posted Here -

https://mayankav.webflow.io/blog/javascript-prototypal-inheritance

Discussion (3)

Collapse
lukeshiru profile image
LUKESHIRU

Best thing about the prototype and __proto__ properties is that nowadays you don't need them at all, at least not directly. If you like coding with classes and methods, you have class, and if you don't like those and prefer a more functional approach (like me), you can just use functions without prototype :D

Collapse
mayankav profile image
mayankav Author

@lukeshiru true that 🙂

Collapse
z00md profile image
z00md

Indeed. It took me months to understand the difference between .__proto__ and .prototype. I understand how JS was built in a hurry but somehow prototypal inheritance(with different constructs) brings in a lot of confusing patterns, specially for people who are coming from classical inheritance. Just for my comfort I call JS an object based language instead of object oriented.

As a side note your 11 take away points summarise prototypal inheritance really well.