Blog

Front End Development topics with a touch of humor

Prototypes are cool

Ahoy! Learn this amazeballs feature of the JavaScript language

September 24, 2020


The classical guitarist Michael Chapdelaine once released an album called "Bach is Cool." Most people don't associate the stern-faced master of baroque music with coolness, but I think Michael was on to something--when you give Bach a chance, you see just how very cool his music is.

Prototypes are the same way. Honest!

I have to admit I have had a difficult time with the concept in the past. When I first learned them in FreeCodeCamp or CodeCademy, I have vague memories of warding them off with anti-vampire gestures, quickly solving the problems, and going on to more pleasant tasks like regular expressions.

The decision seemed validated when the issue just disappeared. As I advanced as a coder, and began to learn React, no one talked about prototypes.

However, then someone on FCC asked a question about them. I limbered up my fingers to answer and help him out and then, to my chagrin, I realized I didn't know the answer. Yes, I could pass the tests, but I didn't understand what I was doing.

My pride was injured. Even though every time I looked at the documentation, my eyes glazed over, I decided that this time would be different.

I was going to figure this prototype stuff out.

Check out the docs on this one

Yet when I girded my loins and visited the MDN page, my second experience was similar to my first. Eye glaze occurred. I began to fidget. I found myself reading the same sentence over and over again.

When that happens, I realize I have to switch learning techniques. Usually I go back to the well, which for me, means getting my hands dirty and coding something.

You see, the MDN docs on this subject are very dry. Let me give you an example:

In JavaScript, a link is made between the object instance and its prototype (its __proto__ property, which is derived from the prototype property on the constructor), and the properties and methods are found by walking up the chain of prototypes.

er...

As mentioned above, the inherited ones are the ones defined on the prototype property (you could call it a sub-namespace) — that is, the ones that begin with Object.prototype., and not the ones that begin with just Object.

Wait, what?

I don't know about you, but that just makes my head hurt. Let's play around and try to figure out how this stuff actually works.

Enter the Pirate

In a dark corner of the Carribbean in the 16th century, there was a very special place that turned out unsavory types who happened to be good sailors. Yes, indeed, it was a pirate factory. Here is how it worked.

            function Pirate(name, age, teeth, skills, motto) {}
        

This is a constructor function. You know because it's capitalized. This sort of function can make new objects when you use the new keyword. Like this:

            const pete = new Pirate("Pete", 41, false, "piracy", "Shiver me timbers!");
        

Now we have an object, pete, with the following properties: {name: "Pete", age: 41, teeth: false, skills: "piracy", motto: "shiver me timbers"}

Great, you say, but what about prototypes? I'm getting to it.

Think of a prototype as a blueprint or a template. The object gets its properties and methods from the blueprint. Not too complicated, right?

If only. Because, you see, the prototype can have its own prototype, which it gets its own methods and properties from. I don't know about you, but this is when I start to get confused.However, if you bear with me, this is why prototypes are cool. They pass down cool stuff to each other, in a prototype chain. This is how it works, under the hood.

For example, we know that pete is named "Pete", he is 41, and he has no teeth, among other sundry facts, but we have not given him any methods. Yet:

            pete.toString()
            //"[object Object]"
        

We can call the toString() method on it! Furthermore, we can:

            pete.valueOf()
            //Pirate {name: "Pete", age: 41, teeth: false, skills: "piracy", motto: "Shiver me timbers!"}
        

Another method for free! How can this be? Well, these methods have been passed down through the prototype. They have been inherited. They are two of the methods passed down from Object.prototype. Object.prototype is the prototype of object--think of it as a kind of bag of tricks that Object has. A treasure chest, maybe--and it gets passed down to baby objects, or pirates...

The metaphor got mixed there, didn't it? We forget to talk about what an Object is, and what that has to do with pete.

Object.prototype

Object is a JavaScript data type. An object is an instance of Object--sort of like I am an instance of Man, my buddy Jupiter is an instance of Dog, or Earth is an instance of Planet. Think of Object is the Platonic ideal of objects, like one of those anatomical illustrations of the human body that is meant to show the general features of all human bodies.

Object.prototype, if you'll remember, is the treasure chest that Object passes down to its lucky descendants. To see what it contains, you can type Object.prototype. (note the period) in the console and see what riches it contains. Otherwise, go to the left-hand side of the MDN docs.

Notice, though, that only the methods that start with Object.prototype are in this chest of which we speak. The others belong to plain old Object. Only the ones in Object.prototype are passed down to mere mortal objects.

And indeed, if we type pete. in the console, we will see the same methods that Object.prototype has. These have been passed down, the heritage of all Object descendants.

Here we come to why prototypes are cool. You can directly modify the prototype object and those changes will trickle down to all other objects from which it descends. Observe.

Playing with prototype

Let's make use of a tricksy, er, trick that I learned from the MDN docs. Did you know you can make an object from any other object, by using its constructor function? Check it out!

            const petey = new pete.constructor('Petey', 10, true, 'squeezing into tight places', 'Are we there yet?');
            
            // Pirate {name: "Petey", age: 10, teeth: true, skills: "squeezing into tight places", motto: "Are we there yet?"}
        

The pirates are multiplying! A peek in the console shows that petey, while being very much his own person, also has the same methods passed down from Object.prototype to pete.

Now... another trick is you can actually modify the Object.prototype object directly. Watch:

            Object.prototype.yoho = function() {console.log('yohoho!')};
            pete.yoho();
            // yohoho!
            petey.yoho();
            // yohoho!
        

I show you this because it's cool, not because you should ever use it. Think about the power you've been given. Just with a single line of code, you've given all objects the power to yohoho.

            const obj = { name: "boring object" };
            obj.yohoho();
            // yohoho!
        

From now on, all objects will have the ability to yohoho. That's power, my friend! But with great power, as they say... Put it this way, do you want to live in a world in which all generic objects know how to yohoho? I mean, I do, but not everyone does. Imagine if you changed Object.prototype but then programmers who came after you had to deal with it. They might find random objects yohohoing for no reason and have no idea what's going on. It would probably be best not to mess with Object.prototype unless there was a really good reason to do so.

See you next time, mateys!