What is a Prototype in JavaScript?

A Prototype is simply a built-in property that exists in all functions that points to an object. (nb: Arrow Functions are an exception, because they don’t have a this keyword)

What is the point of a Prototype?

Prototypes are useful because they allow us to share methods across all instances of a function.

I’m going to demonstrate how things might have been done if prototypes did not exist and how they might be done now. Let me show you what I mean.

Story Time

Back in highschool a lot of my friends wanted to be rappers and so I wanted to create a constructor function that creates objects to represent a few of them. (Don’t worry too much about what the methods do, it’s not really that important, just know that they are there). To be clear, the constructor function called Rapper creates a new object that represents a friend.

function Rapper(name, age) {
    let rapper = {}
    
    rapper.name = name
    rapper.age = age
    
    rapper.album = function(songs) {
      this.songs = songs
      console.log(`Here are the songs on my album: ${songs}`)
    }
    
    rapper.write = function(lyrics) {
      this.lyrics = lyrics
      console.log(`These are my lyrics: ${lyrics}`)
    }
    
    rapper.record = function(lyrics) {
      this.recite = lyrics
      console.log(lyrics);
    }
    
    return rapper;
}

Now let me create some objects that represent my friends

const donzy = Rapper("Donzy", 18);
const daze = Rapper("Daze", 17);
const shimz = Rapper("Shimz", 18);

The problem with the constructor function Rapper is that it is kind of wasteful. Every single time that I create a new instance of Rapper, I am recreating three new methods. This doesn’t make sense because every rapper has the same methods, so we can improve this by creating an object that stores all of the rapper methods.


const rapperMethods = {
  album(songs) {
      this.songs = songs
      console.log(`Here are the songs on my album: ${songs}`)
    },
    
    write(lyics) {
      this.lyrics = lyrics
      console.log(`These are my lyrics: ${lyrics}`)
    },
    
    record(lyics) {
      this.recite = lyrics
      console.log(lyrics);
    }
}
    
function Rapper(name, age) {
    let rapper = {}
    
    rapper.name = name
    rapper.age = age
    
    rapper.album = rapperMethods.album
    
    rapper.write = rapperMethods.write
    
    rapper.record = rapperMethods.record
    
    return rapper;
}

Much much better. However, this could get a little tedius because everytime we add new methods to our object rapperMethods, we have to update our constructor function Rapper. This is where we can use a special friend called Object.create().

What does Object.create() do?

A few things. First of all, it will return us a new object, but that’s just the start. Any object we put in as the first argument into Object.create() will be what the JavaScript engine delegates to when there is a failed look up on the newly created object… Okay, that was probably confusing, so let me show you.


const rapperMethods = {
  album(songs) {
      this.songs = songs
      console.log(`Here are the songs on my album: ${songs}`)
    },
    
    write(lyrics) {
      this.lyrics = lyrics
      console.log(`These are my lyrics: ${lyrics}`)
    },
    
    record(lyrics) {
      this.recite = lyrics
      console.log(lyrics);
    }
}

function Rapper(name, age) {
    let rapper = Object.create(rapperMethods)
    
    rapper.name = name
    rapper.age = age
    
    return rapper;
}

So to demonstrate what Object.create() is doing, let me refer back to one of the three rapper variables I created earlier.

const donzy = Rapper("Donzy", 18);

donzy.name // "Donzy"
donzy.age // 18
donzy.record("Here are my lyrics") // Here are my lyrics

As you can see by donzy.record, record isn’t actually a method on the Rapper constructor function. The reason we are able to access the method record on rapperMethods is because of Object.create(). It allows us to delegate to rapperMethods whenever there is a method that the JavaScript interpreter cannot find on the Rapper function. Hopefully you’re still with me. If you are, the good news is, we’ve arrived to whole point of this blog post.

Tying it all together with Prototypes

Like we discussed earlier, every function already has an object called prototype on them. Remember it’s just an object, no need to complicate it. What we can do now is instead of creating this object called rapperMethods, we can just add all of the methods we want every instance of the Rapper constructor function to have, to the Rapper.prototype.

In addition, after adding all of the methods to Rapper.prototype, instead of delegating to rapperMethods, we can change Object.create() to delegate to the Rapper.Prototype instead. This way we can get rid of rapperMethods all together and just utilize the prototype that our Rapper function already comes loaded with.

Rapper.prototype.album = function(songs) {
      this.songs = songs
      console.log(`Here are the songs on my album: ${songs}`)
    },
    
Rapper.prototype.write = function(lyrics) {
      this.lyrics = lyrics
      console.log(`These are my lyrics: ${lyrics}`)
    },
    
Rapper.prototype.record = function (lyrics) {
      this.recite = lyrics
      console.log(lyrics);
    }

function Rapper(name, age) {
    let rapper = Object.create(Rapper.prototype)
    
    rapper.name = name
    rapper.age = age
    
    return rapper;
}

Now simialr to before, I can access all of the methods on the Rapper.prototype and everytime I add a new method to the prototype, all of my instances of Rapper will also have access to these new methods.

const donzy = Rapper("Donzy", 18);

donzy.record("Here are my lyrics") // Here are my lyrics

// adding a new method to Rapper.prototype

Rapper.prototype.perform = function (rap) {
      this.perform = rap
      console.log(rap);
    }
}

donzy.rap("This is me rapping"); // "This is me rapping"

As you can see, everytime we add new methods to the Rapper.prototype, all instances of the Rapper constructor function will also have access to these methods.

Hopefully you learned as much as I did by writing this post and if you have any questions you can send me a message on my website (prashath.dev) or on my twitter (@prashathc23). Thank you for reading.