DEV Community

Belén
Belén

Posted on

The magical world of JavaScript prototypes

How many times have we heard "JavaScript is not an Object-Oriented language, it's Prototype-oriented"? It turns out it's not accurate.

Here there are some objects in JavaScript, each created in a different way:

({} instanceof Object)
// => true

([] instanceof Object)
// => true

function Foo() {}
((new Foo) instanceof Object)
// => true

So we do have objects in JavaScript. So, what about prototypes? It's the mechanism by which JavaScript implements its Object Orientation. So yes, JavaScript it is a prototype-based, Object-Oriented language.

With the arrival of ES6 classes, some folks may think that it's not worth it to learn how to deal with prototypes. This is untrue for a few reasons:

  1. ES6 classes are basically syntax sugar for prototypes. Instances of ES6 "classes" are still prototype-based.

  2. There is a vast ES5 (i.e. with no classes) codebase around the world, and chances are you will have to deal with it sooner or later.

With all of this, let's learn a bit about JavaScript prototypes, shall we?


A prototype is just an "special object" embedded in an Object. In JavaScript we can access it via the property __proto__:

const witch = { name: "Hermione" }
witch.__proto__
// => {} (empty prototype)

What makes this special is that the prototype acts as some kind of "proxy" or "backup", transparently. If we try to access a property that is not present in an Object, but the prototype does have it, JavaScript will return the prototype's. Continuing the previous example:

// add a property to the prototype
witch.__proto__.spells = { leviosa: "Wingardium leviosa" }

// the property is not defined by the object…
witch
// => { name: "Hermione" }

// …but we can access it thanks to the prototype!
witch.spells
// => { leviosa: "Wingardium leviosa" }

What's the practical application of this? To share code among Objects. In Object-Oriented languages which have classes, the class acts a "template" that is shared among all the instances of that class. In JavaScript, there is no "template": what we have is a shared common object, the prototype.

We can easily see this when we instantiate objects using a constructor function. If we have a Wizard function, each time we create a new object with new Wizard(), what's defined in the property Wizard.prototype is established as the prototype of the newly created instances.

function Wizard(name) {
  this.name = name || "Anonymous"
}

Wizard.prototype.spells = {
  leviosa: "Wingardium leviosa",
  expelliarmus: "Expelliarmus",
  patronus: "Expecto patronum" 
}

const draco = new Wizard("Draco")
// => Wizard { name: "Draco" }
const hermione = new Wizard("Hermione")
// => Wizard { name: "Hermione" }

draco.spells === hermione.spells
// => true (both wizards share spells)
draco.__proto__ === hermione.__proto__
// => true (that's why they share prototypes)
hermione.__proto__ === Wizard.prototype
// => true (their prototype is defined in Wizard.prototype)

The benefits of sharing this common object –the prototype– are:

  • To avoid duplication in memory, since the prototype is shared by all the Objects that need it, instead of each one having a replica of it.
  • To be able to modify multiple objects on the fly in a go, by modifying the prototype.

Thanks to this system, we can also modify only specific Objects, by adding properties that only them have. If this property has the same name of a property in the prototype, the one contained directly in the Object will have precedence. For instance, we could have a first-year student in Hogwarts with an empty spellbook:

const newbie = new Wizard("Lorem")
newbie.spells = {} // bypass what's in the prototype

newbie.spells === hermione.spells
// => false

And now let's imagine that in the Wizarding World a huge discovery has been made, and they have learned conjure up authentic horchata on demand. We could easily update everyone's spellbook –as long as it has not been previously overridden–, by simply altering the prototype itself.

// add a new spell
Wizard.prototype.spells.horchata = "Send horchata"

// check Hermione's spellbook
hermione.spells
// => { leviosa: "Windgardium leviosa",
//   expelliarmus: "Expelliarmus",
//   patronus: "Expecto patronum",
//   horchata: "Send horchata" }

This is a very powerful feature, but thanks to Marvel we all now that with great power comes great responsibility. Even more in JavaScript, since it's too easy to deeply mess with prototypes. How far can we go? Well, we can even alter the prototype of objects that are part of the standard library, like Object, Date, Array… Here's a hacky example, which I have named the "Flamenca Trick":

Date.prototype.toString = () => "💃"
`${new Date()}`
// => 💃

I hope you enjoyed this short intro to JavaScript prototypes. Happy coding!

Discussion (4)

Collapse
mustapha profile image
Mustapha Aouas • Edited on

For poeple that are wondering what's the difference, prototype is the object that is used to build __proto__ when you create an object with new.

Collapse
73nko profile image
73NKo

Great article! I love the new horchata spell!

I have a question that maybe its not very related the article but I think so. Some people thinks despite javascript has this inheritance through proptotype chain is better use composition with a factory function. What do you think about that? I use to have discussions gorila-banana problem vs better performance. What would be in your opinion a better approach?

Collapse
ladybenko profile image
Belén Author

Thanks!

I'm more keen to use composition over inheritance unless I really need a different type (and in this case, I try not to have a chain, but just a single subtype), but I'm afraid prototypes have nothing to do with this. Composition vs Inheritance is a debate that extends to every Object-Oriented language, independently if they have prototypes or not.

Collapse
lenaalcie_18 profile image
Lena Tevar

I'm studying js and I love it, it makes such more sense now.