Don’t be afraid of prototypes

Photo by Daniel Gregoire on Unsplash

I remember well the first time I encountered prototypes in JavaScript. It was in the very beginning of my programming journey and when I started to dig deeper into this concept, I simply came to conclusion: ‘Well… classes do the same thing, so I’m not going to bother…’. My somehow superficial grasp of OOP let me to create simple programs but it didn’t take me too far, as I started to encounter bugs that I couldn’t understand and fix. At Launch School learning about prototypes became unavoidable, so I had to give it a go.

This concept was, by far the most complex I have encountered (execution context was pretty fun too). I recall the inexplicable urge of throwing my laptop through the window instead of spending any more second on ‘trying’ to understand how JavaScript works. The more I was reading, the more confused I was getting, as I was being introduced to prototypes, __proto__, [[Prototype]] , prototype property, function prototypes and object prototypes (which are by the way not the same thing). I must admit that the person who introduced all those terms to JavaScript could have been slightly more creative while choosing names.

However, whoever you are who reads this article I want to let you know that there is a light in the end of the tunnel! Understanding JavaScript prototypes is actually not a distant and unreachable goal as it may seem at first. It’s actually pretty simple! Wait! Before you start throwing rotten tomatoes at me for saying that, just give me a chance! I promise we will fight this beast together!

Photo by DDP on Unsplash

Why are prototypes so confusing?

Let’s start with the understanding, why so many people find prototypes so confusing. There are two reasons to that. First of all, if anyone has previously learned about OOP in other object oriented languages, this one is obvious. It’s confusing because it’s different. JavaScript doesn’t use a typical class inheritance as other languages do, so for this reason if you did learn about OOP in other languages it’s the best to forget about it and approach JS’s prototypes with the clean slate and open mind. The second reason is that the word ‘prototype’ in JavaScript actually refers to several things. In this article I will make attempt to explain all those confusing terminology and demonstrate how it’s related.

[[Prototype]]

Let’s start with this ‘weird’ one, that you have probably seen before. What is this mysterious [[Prototype]] and what’s it’s function?

Every time we create an object, it automatically gets this extraordinary ‘gift’, which is a [[Prototype]] property. If you look for it, you won’t find it, since it’s hidden. You cannot see it, you cannot touch it, you cannot smell it, but it’s there on every object, and it’s very powerful, which we will witness in a moment. I invite you to code along with me.

Let say we have created and object babyYoda:

let babyYoda = {};
babyYoda['age'] = 'no one knows';
babyYoda.hasOwnProperty('age') // => true
babyYoda.hasOwnProperty('[[Prototype]]') // => false
Photo by Jonathan Cooper on Unsplash

As you can see our ‘babyYoda’ doesn’t really have much and cannot do much yet. It just has the ‘age’ now and a hidden ‘[[Prototype]]’ property, that we are not able to see. Let say, we would like him to to able to do telekinesis. There are many Jedi who also uses this force, so it would make sense to re-use it and not create this property directly on ‘babyYoda’. In order to do that we have to give him access to all this knowledge somehow. Where would he go to acquire such a wisdom? Of course to Jedi! Let’s create an object that has all those great forces:

let jediForces = {
telekinesis() {
console.log('moving objects with thoughts')
},
instinctiveAstrogation() {
console.log('find a route through hyperspace');
},
flowWalking() {
console.log('change the course of the future')
},
//all the other Jedi forces...
};

At this moment ‘babyYoda’ cannot really do telekinesis:

babyYoda.telekinesis(); //TypeError: babyYoda.telekinesis is not a function

How can we make ‘babyYoda’ do all those amazing thing? We could just simple create a ‘telekinesis’ method on ‘babyYoda’ object. However, this would be redundant if we already have an object that has it’s own ‘telekinesis’ that does exactly what we need. Imagine that we have several Jedi who also like to use telekinesis. That wouldn’t really make sense to define this method on each one of them. Instead, we will delegate access to this method. Let’s have a look at this code snippet:

Object.setPrototypeOf(babyYoda, jediForces);babyYoda.telekinesis(); //’moving object with thoughts’babyYoda.hasOwnProperty('telekinesis'); //falsejediForces.hasOwnProperty('telekinesis') //true

That is simply magical, but how did it happened? This is the power of ‘[[Prototype]]’! What we did with ‘Object.setPrototype()’ is: we used it to set the hidden ‘[[Prototype]]’ property to points to ‘jediForces’ and with that we created a connection between those two objects. This connection that gives one object ability to access methods of another object we call a prototype chain. Now ‘babyYoda’ can use all this fantastic powers without actually having them specifically created on himself, thanks to the prototype chain, that connects him to all the ‘jediForces’! You can imagine ‘[[Prototype]]’ to be like a signpost that tells one object where to search for the powers/functionalities/methods that this object wants to have access to.

Photo by Hansjörg Keller on Unsplash

How is that possible and how does it work? Whenever we call a method on an object, JavaScript will firstly search for this method on the object itself. If not found, it will look at ‘[[Prototype]]’ and what it’s pointing to and search in the next object in the prototype chain. If not found JS will continue until the end of the prototype chain. What object is in the end of the prototype chain? Unless specified otherwise, all objects have ‘Object.prototype’ at the end of the chain. The hidden [[Prototype]] of Object points to null. We will come back to what this mysterious prototype property on Object is in a moment. You can imagine the babyYoda's prototype chain as so:

There are several ways we can connects object through prototype chain. We’ve seen already Object.setPrototypeOf()in action, but we could also use Object.create()that creates and returns an entirely new object that has it’s internal [[Prototype]] property set to point to another object that we pass as an argument. We could also use newkeyword, that calls a function as a constructor. (we are not going to cover it here). There is also a __proto__property. Since it’s deprecated we won’t talk about it much but for the clear picture it is useful to understand what it is and how it works.

__proto__

__proto__ called also dunder proto, (since it has double underscore) is a specific type of property that allows us to view and set the object that [[Prototype]]is pointing to.

console.log(babyYoda.__proto__ === jediForces) // => true

Let’s try to change what babyYoda ’s [[Prototype]] points to using __proto__:

let darkSideOfTheForce = {
killEveryone() {
console.log('kill everyone!!!')
}
}
babyYoda.__proto__ = darkSideOfTheForce;
babyYoda.killEveryone(); //=> kill everyone!!!
babyYoda.telekinesis(); //TypeError: babyYoda.telekinesis is not a function

Well, that’s not what we want babyYoda to do. Let’s give him back access to jediForces.

babyYoda.__proto__ = jediForces;babyYoda.telekinesis(); //=> ‘moving objects with thoughts’

Although dunder proto is quite handy, it is deprecated, thus not a recommended way to set and access what [[Prototype]] is pointing to. Use Object.getPrototypeOf() and Object.setPrototypeOf() instead.

Prototypes and Prototypal Inheritance

Most of the time when we talk about prototypes, what we mean is an object, that other objects inherits from. In OOP, Inheritance is the mechanism of basing a class upon another, creating parent-child-like relationship. Because of prototypal nature in JS inheritance works more as a delegation since inheritance allows a ‘child’ object to have access to methods and properties of it’s ‘parent’ object (and all objects that are included in it’s prototype chain) without explicitly defining them on the ‘child’ object. If you’ve encountered inheritance in other languages like Java, that may be a surprise. In JavaScript we don’t copy any methods. Rather JavaScript delegates to those properties through prototype chain. This special type of inheritance is called prototypal inheritance, but is in fact nothing else but delegation. It’s important to remember that whenever we make changes on the method, all objects that have access to this method will see that change as well. In our Star Wars example the jediForces object is a prototype of babyYoda(babyYoda inherits from jediForces)since babyYoda has access to methods from jediForces. Objects like jediForces we also call object prototypes, since it’s what object’s [[Prototype]] property points to.

jediForces.telekinesis = function() {
console.log('moving objects with power of the mind');
}
babyYoda.telekinesis(); // => moving objects with power of the mind

We can find out which object is a prototype of another object using ‘Object.getPrototypeOf()’:

console.log(Object.getPrototypeOf(babyYoda)); //=>// {
// telekinesis: [Function],
// instinctiveAstrogation: [Function: instinctiveAstrogation],
// flowWalking: [Function: flowWalking]
// }

We can create endless prototype chain and the objects that is in the beginning of it will have access to all of the properties of all the objects in the prototype chain. Usually each prototype chain will end with Object.prototype and then null. Let’s have a closer look at what exactly prototype property is.

Build-in Objects

You might have encountered in the documentations that some of the method’s names look like Array.prototype.slice() while other look like Object.create(). What is the difference between one and the other?

In JavaScript all functions are actually a special type of object. This feature allows us to treat functions as any other value: pass it around, return it or take as argument. Since functions are objects they also have properties. One property that function objects have is a prototype property. This is a very important property as it is used to refer to an object that stores different functionalities. It’s what makes functions special as prototype property only exists on this type of object. The object, that prototype property points to, we also call a function prototype. Since it’s a prototype (we use it to share functionalities) and a property on a function.

When you look at the MDN documentation you will find a couple of built-in objects such as Array, Object or String. They are all constructor functions and thus have a prototype property. We will not go deeper into what ‘constructor’ is, but in short it’s a function that we use to instantiate many objects of the same type. Let’s have a look at how Array look like. We can use Object.getOwnPropertyNames(Array) to view names off all ‘own’, not hidden properties of an Array build-in object:

Object.getOwnPropertyNames(Array); // => [ 'length', 'name', 'prototype', 'isArray', 'from', 'of' ]

Here we see all ‘own’ properties of Array object. You might already recognize some of them. Those are properties that we call directly on the object itself like so: Array.isArray(). Did you spot the prototype property? Let’s have a look on what is this mysterious prototype property is pointing to:

Object.getOwnPropertyNames(Array.prototype);//=>['length',      'constructor',    'concat',
'copyWithin', 'fill', 'find',
'findIndex', 'lastIndexOf', 'pop',
'push', 'reverse', 'shift',
'unshift', 'slice', 'sort',
'splice', 'includes', 'indexOf',
'join', 'keys', 'entries',
'values', 'forEach', 'filter',
'flat', 'flatMap', 'map',
'every', 'some', 'reduce',
'reduceRight', 'toLocaleString', 'toString'
]

Does any of that look familiar? How about if we do the same with Object.prototype? Did you spot __proto__? You can also try String.prototype or Number.prototype. Here we have all those methods that we are using on a daily basis! Isn’t that magical?

Let’s experiment a bit. What do you think will happen here?

Object.prototype.find = function () {
console.log('finding a life purpose')
};
let lifeMeaning = {};
lifeMeaning.find(); // => ???

Wait a moment… We called find method on lifeMeaning , shouldn’t that return a TypeError since lifeMeaning is just an empty object? What is happening here?

Do you remember what is the last object at the end of the lifeMeaning ‘s prototype chain? Yes! Object.prototype ! The object that stores all the methods that we are able to access! We have simply added a new method to the object that Object.prototype is referencing. Array also have the prototype property and the object it’s pointing to stores all the methods such as split(), slice(), forEach() and therefore any array will have access to those methods, even though we have never explicitly defined any of those functionalities! Isn’t it truly incredible?

As you continue to explore the object oriented side of JavaScript, it’s important to keep in mind that the prototypal nature of JavaScript will be noticeable every step of the way. Polymorphism, inheritance and encapsulation will not work as they do in other OO languages. Likewise the ‘class’ syntax, introduced with ES6, is just syntactic sugar hiding what is actually happening under the hood. Even though for some developers understanding prototypes seems unnecessary and many of them grow a great deal of grey hair while trying to slay this beast, sooner or later they realize that it’s unavoidable. Prototypes are here to stay, so don’t ignore them praying that they will quietly leave from under your bed.

Photo by Yaopey Yong on Unsplash

Professional Problem Solver