DEV Community

Cover image for The Prototype Pattern in JavaScript
jsmanifest
jsmanifest

Posted on

The Prototype Pattern in JavaScript

Find me on medium

There are multiple design patterns that can be implemented in the JavaScript language, and in this post we will be going over the prototype design pattern.

The prototype design pattern is an object-based creational design pattern.

If you need a recap on the three types of design patterns that they are generally coincided with, here is a little overview:

  1. Creational Design Patterns

Instead of you having to directly instantiate objects directly, these are the ones that create them for you. The benefit of this approach is that it gives your program a little more flexibility when deciding which objects need to be created for certain situations.

  1. Behavioral Design Patterns

These patterns are focused on the communication between objects.

  1. Structural Design Patterns

And lastly, these patterns focus on class and object composition. They can be used to compose interfaces through inheritance and define ways to compose multiple objects in order to achieve new functionality.

If this is your first time learning about the prototype pattern, you now might have some idea of what to expect. But if you don't then it is my job to help clear that mystery for you, my friend.

So what exactly is the prototype pattern, and what does it do?

thinking

This pattern's main focus is to help create objects that can be used as blueprints for any object that are created by constructors. It does this through what's called prototypal inheritance.

Since JavaScript has native support for prototypal inheritance, it fortunately becomes naturally easy to work with in the language to the point where you don't really need to learn any new concepts but the syntax itself.

With that said, the prototype design pattern is a very useful strategy--which makes it an important and beneficial way to create programs in JavaScript. We will see why in a bit.

When objects are created through the constructor function and contains the name property, then further objects created with the same constructor function will also have this same property as shown below:

function Movie(title) {
  this.title = title
}

const harryPotter = new Movie('Harry Potter')
const rushHour2 = new Movie('Rush Hour 2')
const fastAndFurious = new Movie('Fast And Furious')

console.log(harryPotter.constructor.name)
console.log(rushHour2.constructor.name)
console.log(fastAndFurious.constructor.name)
Enter fullscreen mode Exit fullscreen mode

It sounds like typical class objects, but in reality it avoids using classes altogether. The prototype design pattern simply creates copies of existing functional objects as opposed to defining brand new objects.

The biggest benefit of using the pattern in JavaScript is the performance boost gained as opposed to object oriented classes. This means that when you define functions inside an object, they will be created by reference. In other words, all child objects will point to the same method instead of creating their own individual copies!

Here's a code example of the pattern in action:

const Warrior = function(name) {
  this.name = name
  this.hp = 100
}

Warrior.prototype.bash = function(target) {
  target.hp -= 15
}

Warrior.prototype.omniSlash = function(target) {
  // The target's hp may not be under 50 or this attack will fail on the opponent
  if (target.hp < 50) {
    return
  }
  target.hp -= 50
}

const sam = new Warrior('Sam')
const lenardo = new Warrior('Lenardo')

sam.bash(lenardo)
Enter fullscreen mode Exit fullscreen mode

In our code example, we defined a warrior's attack methods by using Warrior.prototype.<method> = function() {...}. You can see that we instantiated some warriors with the new keyword so now we are looking at two instances. Both instances set their name property according to the name argument that was passed in by the caller.

When we defined the methods bash and omniSlash on the prototype as demonstrated, the two separate instances we're looking at are actually referencing the same bash and omniSlash functions!

const Warrior = function(name) {
  this.name = name
  this.hp = 100
}

Warrior.prototype.bash = function(target) {
  target.hp -= 15
}

Warrior.prototype.omniSlash = function(target) {
  // The target's hp may not be under 50 or this attack will fail on the opponent
  if (target.hp < 50) {
    return
  }
  target.hp -= 50
}

const sam = new Warrior('Sam')
const lenardo = new Warrior('Lenardo')

console.log(sam.bash === lenardo.bash) // true
Enter fullscreen mode Exit fullscreen mode

If we instead defined them like this, then they are not the same, so essentially JavaScript has created another copy of the supposedly same method for each instance:

const Warrior = function(name) {
  this.name = name
  this.hp = 100

  this.bash = function(target) {
    target.hp -= 15
  }

  this.omniSlash = function(target) {
    // The target's hp may not be under 50 or this attack will fail on the opponent
    if (target.hp < 50) {
      return
    }
    target.hp -= 50
  }
}

const sam = new Warrior('Sam')
const lenardo = new Warrior('Lenardo')

console.log(sam.bash === lenardo.bash) // false
Enter fullscreen mode Exit fullscreen mode

So if we didn't use the prototype pattern like the last example, how crazy would it be when we instantiate many instances? We would have cloned methods cluttering up memory that essentially do the same exact thing, who don't even need to be copied unless it relies on state inside instances!

Another variation of extending prototypes is a syntax like below:

const Warrior = function(name) {
  this.name = name
  this.hp = 100
}

Warrior.prototype = {
  bash(target) {
    target.hp -= 15
  },
  omniSlash(target) {
    // The target's hp may not be under 50 or this attack will fail on the opponent
    if (target.hp < 50) {
      return
    }
    target.hp -= 50
  },
}
Enter fullscreen mode Exit fullscreen mode

Which is equivalent to:

const Warrior = function(name) {
  this.name = name
  this.hp = 100
}

Warrior.prototype.bash = function(target) {
  target.hp -= 15
}

Warrior.prototype.omniSlash = function(target) {
  // The target's hp may not be under 50 or this attack will fail on the opponent
  if (target.hp < 50) {
    return
  }
  target.hp -= 50
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

And that concludes the end of this post! I hope you found this to be valuable and look out for more in the future!

Find me on medium

Top comments (6)

Collapse
 
emptyother profile image
emptyother

..which is equivalent to the code below, a typical class object in modern javascript. I prefer this over modifying prototype objects directly, just because it looks cleaner.

class Warrior { 
    constructor(name) {
        this.name = name;
        this.hp = 100;
    }
    bash(target) {
        target.hp -= 15;
    }
    omniSlash(target) {
        if(target.hp < 50) {
            return;
        }
        target.hp -= 50;
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
lukaszs1 profile image
Lukasz

it is just syntactic sugar for above, which is very nice explanation of prototype pattern

Collapse
 
appalanaveen1 profile image
appalanaveen1

I am an experienced java developer trying to learn javascript, this post helped me a lot to understand the inheritance used in javascript, i read several posts, but everyone says "JavaScript is a bit confusing for developers experienced in class-based languages (like Java or C++)" and continue with syntax and examples etc..., but never told about what's the exact difference, here only i got exact answer what i am looking for days. Thanks a lot.

Collapse
 
jsmanifest profile image
jsmanifest

That is awesome, your welcome!

Collapse
 
fly profile image
joon

Very informational read, never understood why some experienced devs used prototypes, now I do and plan on doing the same. Thanks a lot for shedding light on the subject

Collapse
 
jsmanifest profile image
jsmanifest

Very glad to hear that!