Challenge: Solution Review
This lesson will explain the solution to the problem from the last coding challenge.
We'll cover the following
Solution #
const Ninja = function(name) {this.points = 100this.name = name}Ninja.prototype.punch = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 20return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't punch ${otherNinja.name}`}}Ninja.prototype.kick = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 50return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't kick ${otherNinja.name}`}}const ninja1 = new Ninja("Ninja1")const ninja2 = new Ninja("Ninja2")console.log(ninja1.kick(ninja2))console.log(ninja2.punch(ninja1))console.log(ninja1.kick(ninja2))console.log(ninja1.punch(ninja2))console.log(ninja2.kick(ninja1))
Explanation
In this challenge, you had to create a small game for ninja fighting using the prototype pattern. Two ninjas, ninja1
and ninja2
, fight each other. They either have the option to kick
or punch
each other. Let’s look at how we achieved this using the prototype pattern.
First, we define the Ninja
constructor function. It accepts the name
parameter and sets the name
property of the current object equal to it. Next, it defines the points
property for the instantiated object and sets it equal to 100
.
this.points = 100
this.name = name
The next step is to define the punch
and kick
functions. One way to do that is as follows:
const Ninja = function(name) {this.points = 100this.name = namethis.punch = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 20return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't punch ${otherNinja.name}`}}this.kick = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 50return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't kick ${otherNinja.name}`}}}const ninja1 = new Ninja("Ninja1")const ninja2 = new Ninja("Ninja2")console.log(ninja1.kick(ninja2))console.log(ninja2.punch(ninja1))console.log(ninja1.kick(ninja2))console.log(ninja1.punch(ninja2))console.log(ninja2.kick(ninja1))
As you can see, this approach works completely fine and produces the correct output. So what’s wrong with it?
In the above solution, both the instances ninja1
and ninja2
get their copies of the punch
and kick
function upon instantiation. Imagine making a 1000 instances that would mean a 1000 copies of both these function for all 1000 instances! This will clutter the memory very easily.
const Ninja = function(name) {this.points = 100this.name = namethis.punch = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 20return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't punch ${otherNinja.name}`}}this.kick = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 50return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't kick ${otherNinja.name}`}}}const ninja1 = new Ninja("Ninja1")const ninja2 = new Ninja("Ninja2")console.log(ninja1.kick === ninja2.kick)console.log(ninja1.punch === ninja2.punch)
As you can see from the output of the code above, both ninja1
and ninja2
have different copies for both the punch
and kick
functions.
As explained in a previous lesson, the prototype pattern is supposed to boost the performance and make the code more efficient. Hence, you might have noticed that in our solution, both the functions are defined on the prototype
of Ninja
.
Ninja.prototype.punch = function(otherNinja) {
//some code...
}
Ninja.prototype.kick = function(otherNinja) {
//some code...
}
So why have we done this? Since we have defined the functions on the prototype
, both ninja1
and ninja2
instances refer to the same punch
and kick
functions; instead of having their copies of each. This makes the code more memory efficient.
const Ninja = function(name) {this.points = 100this.name = name}Ninja.prototype.punch = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 20return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't punch ${otherNinja.name}`}}Ninja.prototype.kick = function(otherNinja) {if(otherNinja.points > 0 && this.points > 0){otherNinja.points -= 50return `${otherNinja.name}'s points are ${otherNinja.points}`}else{return `Can't kick ${otherNinja.name}`}}const ninja1 = new Ninja("Ninja1")const ninja2 = new Ninja("Ninja2")console.log(ninja1.kick === ninja2.kick)console.log(ninja1.punch === ninja2.punch)
From the output of the code above, you can see that both the instances have the reference to the same kick
and punch
functions.
The implementation of both these functions is simple. They accept otherNinja
object as a parameter.
Ninja.prototype.punch = function(otherNinja)
Ninja.prototype.kick = function(otherNinja)
Next, they check if the points
of both the ninjas are greater than 0
.
if(otherNinja.points > 0 && this.points > 0)
If either of these conditions is false a message stating that the ninja can’t proceed with the attack is returned.
//punch function
//.....some code
return `Can't punch ${otherNinja.name}`
//kick function
//some code...
return `Can't kick ${otherNinja.name}`
If both these conditions are satisfied, then the ninja is allowed to attack.
-
otherotherNinja
'spoints
are reduced by20
if it is a punch.otherNinja.points -= 20
-
otherotherNinja
'spoints
are reduced by50
if it is a kick.otherNinja.points -= 50
Let’s discuss the abstract pattern in the next lesson.