Photo by Jeremy Bishop on Unsplash

Prototypes are one of the captivating topics in JavaScript that best represents one of the programming main conventions, namely, DRY (do not repeat yourself). This is about inheriting from parent to many siblings by both keeping common properties intact and adding something distinguishing, intrinsic to the instance itself (common + specific). We can find these examples throughout nature and real life. For instance, all human beings share many things in common, they have limbs, names, age, skin color, and other properties, whereas the specific name or the specific age may differ. All dogs have plenty in common, like legs, ability to bark and run, etc., but breeds, colors, and age may differ. Everything will become even clearer when we translate the word “prototype” from Latin, where “proto” and “typos” mean “first” and “kind”, respectively. So this is the ‘first kind’, the root of the tree that all the branches inherit from.

It’s also important to remember that the inheritance model in JavaScript is prototypal.

new

For the below-mentioned examples, we will use ES2016 standard syntax with class keyword (because this syntax is newer and looks nicer), you can always use function-s as a matter of personal preference though. With the use of the new keyword, it is possible to create a new instance of Prototype, leaving the common, original object intact, meanwhile adding some own (new) properties passed as arguments into instances upon creation as the example below illustrates.

If we invoke born() function on jane instance, it will give us ‘I was born in 1995’. And there is nothing magical happening here. Upon creation of jane instance it was bound to Person constructor (typeof jane ‘object’, but jane.constructor [Function: Person]). Within Person constructor, we have born function, which is bound to the instance of constructor by this keyword. We should keep in mind though, that this directs to any instance it’s called upon, for instance, if we call jane.born(), it refers to jane instance, but when we call tom.born(), it will now refer to tom instance and to any other instance it’s called for. So this keyword dynamically represents each instance it’s called for. We added some action inside of the function by placing the current year inside of year variable, thus making it possible to do the extraction of instance’s age from the current year and getting the exact date when the jane was born. It’s also important to underline here, that for passing data dynamically, we use backticks (``) with ${} inside of them, to wrap the dynamic data. The last line in our code insinuates something significant! It says, that despite jane and tom instances are different (jane === tom // false) and thus require additional spaces in memory, as long as they share common functionality inherited from their Prototype (sayName() or born()), their functions don’t require extra space (tom.sayName() === jane.sayName() // true). The latter example beautifully proves cost-efficiency of Prototypes, when we only pay once and then benefit from those functions later for free!

Let's create another Prototype where we can invoke a function that capitalizes each word in a given string (assuming we’re too lazy to write each letter of a new word holding shift button on our keyboard)

In the example above, we pass arguments in the instance (title, director) which are passed further as arguments into capitalizeEach function, which takes those arguments, performs a whole bunch of useful stuff, like splitting each word in the place of space (‘ ‘), iterating over those words and capitalizing each word and then returning those words string by join-ing them again in the place of space (‘ ‘). From now on, every word written in Movie instance as an argument will be transferred in its capitalized version for nicer user experience!

Conclusion

JavaScript Prototypes represents all the shared data for all their children, or instances. Imagine all the nice stuff that can be done by those awesome Prototypes. As a programmer, we only create the Prototype once (maybe adding some more data and functionality later on) and each time, when new instance created (by new keyword), it not only has some unique parameters of its own but also can share all the useful functions inherited from ‘first kind’, the Prototype!