You can't get very far in JavaScript without
dealing with objects. They're pretty foundational to almost everything
that we do in the JavaScript programming language. So, what we're going to do in this video is,
we are going to look at a few different patterns for instantiating new objects, and in doing
that we're going to get a crystal clear understanding of what JavaScript's prototype is. So let's start off pretty slow here. In JavaScript, the most common way to create
new objects is with the curly braces, right? Now, if we want to add properties to this
object, we can do that using dot notations, this is something you've probably seen thousands
of times now. And then if we wanted to add an e-methods
on to this object, I'll just paste this in here. We can do that just by adding new properties
to the animal object. And then probably eventually what would happen
is you would need in your application to create multiple animals. So what you would do is you would take all
of this logic, and you will wrap it inside of a function. So we can have a function here called animal,
whoa, animal we can have it take in name and an energy level, and then we can paste all
of this inside of here. And then now instead of hard coding these,
we'll just say animal.name is name and animal.energy is energy. And now we just want to make sure that we
return animal down here. So now whenever we want to create a new animal,
all we have to do is invoke our animal function, pass it a name as well as an energy level. So here we have two different animals, one
called Snoop, one called Leo, and there we go. So, up until this point, this has probably
been pretty boring, right? So all we're doing here is we have a function,
it creates an object and then eventually just return us that object after it's added some
properties to it, nothing too exciting, but can you think of any problems with what we
have so far. So right now when we just have two animals,
it's not that big of a deal, but imagine our application was...imagine we were building
like a Noah's Ark game, or something, that's a really lame example, but that's what I'm
gonna go with. So imagine we were building that app, we would
have lots of different animals inside of our application. So what we're doing right now is every time
we create a new animal, we are recreating in memory each of these specific methods:
eat, sleep, and play. What if instead of recreating those every
time, we created a new instance? Instead, we put these methods somewhere because
these are very generic. They don't need to be specific per instance,
right? All of these because we're using to this keyword
in here can be shared across all instances because they're not specific to any one animal,
they're specific to all animals. So what we can do is let's move all of these
methods out to their own object, and then inside of here we can just reference that
objects. What that'll look like is what if we do something
like this, create an animal, methods, objects, and then inside of here, we are going to have
these three methods. We'll need to format this a little bit. We can get rid of the function keyword, we
can also get rid of animal, and then we need to add commas between each of these methods. So we have eat, sleep, and then play, so that
looks pretty good. And then now inside of animal instead of adding
and recreating these methods each time, we invoke animal, what we can do, is we can come
in here and say animal.eat is just going to be a reference to animalmethods.eat. And then we'll do the exact same thing for
sleep and play. We'll say animal.sleep is a reference to animalmethods.sleep,
and then finally animal.play, is just a reference to animalmethods.play. So now we've solved this solution, we only
ever create animal methods once in memory. And then any time we create a new animal,
what we do, is we give an animal the ability to eat, sleep, and play, but we're just referencing
our animal methods object instead of recreating those every time. Now, can you think of any problems or any
downsides with what we're doing currently? Well, it's not a huge issue but let's say
we came in here and we added like poop, right, animals need to poop that's reasonable. Now, what we'd have to do is we'd have to
remember to go into the animal definition, and then add this line right here animal.poop
equals animalmethods.poop, right? So instead of doing that, what we want to
do, is we want to say, "Hey, we want animal to always be able to reference any of the
methods that are located in the animal methods object." To do this, what we'll use is, a feature of
JavaScript called object.create, so let's go ahead and take a look real quick at what
object.create does. So let's say we had a object here, we'll call
it parent, it has a name, age, and a heritage, and then let's say we wanted to create a child
object. The important thing here is we want child
to be able to have the exact same heritage as the parent. So what we can do is we can say child is going
to be the invocation of object.create that's going to return us a brand new object. So child is going to be the object and here's
the cool part, whatever we pass in as the first argument to object.create, whenever
there is a failed lookup on the child object, the JavaScript engine is then going to delegate
to the parent object. So that was a lot of words, let me show you
what's happening here. So let's make sure we give child its own name
and its own age, so now, if we console.log child.name, we're going to get Ryan. If we log child.age, we're going to get 7. And then if we log child.heritage, what's
gonna happen is JavaScript is gonna say, "Hey, does child have a heritage property?" It doesn't, so let's go ahead and look at
the parent object to see if the parent object has that property. So if we log child.heritage even though child
again doesn't have the heritage property, because we've used object.create passing in
parent, the interpreter, the JavaScript engine will delegate that lookup to the parent object
and then we will get Irish. So there we go, so what we can do now, is
instead of all of these right here where we say animal.eat, animal.sleep, animal.play,
when we create the animal object instead of it just being an empty object just as we barely
saw with the parent and the child, we can say go ahead and create an object that's going
to delegate to animal methods on any failed lookups. So down here now when we run leo.name that's
going to give us Leo, because Leo has a name property. But if we try to do…let's just do leo.play,
the JavaScript engine is gonna say, "Okay it looks like Leo doesn't have a play property,
Leo only has a name and an energy. Let's go ahead and check animal methods to
see if play exists on animal methods." It will, and then it'll invoke this function
right here. So before our problem was that if we added
a method to animal methods, we wanted that to automatically be included in all of the
different animals. And now because we are using object.create,
no matter what method we add to animal methods, when there is a failed lookup on any animal,
the JavaScript engine will then delegate that lookup to animal methods, and then if it finds
it, it will invoke the individual method. All right, there are still a few more ways
that we can improve this code, and this is where it starts getting cool, as cool as instantiation
patterns in JavaScript can get. This seems like such a common pattern that
we would run into that there would be a built-in way in the JavaScript language to do this,
right? Instead of having to manage our animal methods
object which contained all of these shared methods across all of the different animals,
it seems like that it would just be a better place to put this, right, something to do
with the function itself. And this is where JavaScript's prototype comes
into play. So what a prototype is, is it's just a property
on a function. So here we have a function, it's not doing
anything. You'll notice if we do imAFunction.prototype
that gives us back an object with just a constructor property on it. So all a prototype is in JavaScript is it's
just a property on an object that points…is it's just a property on a function that points
to an object. Again, I'm gonna say this again because so
many different resources out there make this way harder than it needs to be. All the prototype is, and if you got this
question in a job interview, this is all I want you to say, to say, "Hey what's a prototype
in JavaScript?" All you have to say is a prototype is just
a property that every function you create in JavaScript has that points to an object. That's all it is. Prototype is a property on a function that
points to an object. So what if instead of creating this separate
object here which contains all of our methods, what if we just put all of these methods on
the functions prototype? Don't overthink that. Prototype is just an object that every function
has, so instead of having to manage our animal methods separately, what if we just go ahead
and put those on the prototype. So let's see what that would look like. So we know that every function has a prototype,
so we could say, animal.prototype.eat is going to be a function. Animal.prototype.sleep is going to be a function,
and then animal.prototype.play is going to be a function. So now let's just go ahead and copy these,
eat takes in an amount. That looks good. We'll do the exact same thing for sleep which
takes in a length. And finally we have play which also takes
in a length. Now what that allows us to do, is we can get
rid of our animal methods. And then now the only thing we have to change
is right here we are saying go ahead and delegate to animal methods. But now we want to say, "Go ahead and delegate
to animal.prototype." So just so I can show you that this is working,
let's go ahead and paste this into our console. Okay, that looks good, now let's create our
instance of animal. So now if we look at Leo, Leo has a name and
energy but because of this line right here, when we do leo.play, the JavaScript engine
is gonna say, "Okay, does Leo have a play method. It doesn't, so let's look to animal.prototype
to see if animal.prototype has a play method." So then it looks to animal.prototype, we have
a play method so then it runs that method right there, and we get Leo is playing. So the big takeaway from what we've learned
so far is that prototype is just a property that every function in JavaScript has, and
as we just barely saw, it allows us to share methods across all instances of a function,
and an instance being individual animals like Leo or Snoop. All the functionality is still the same but
now instead of having to manage a separate object for all the methods like we had earlier,
we can just use another object that comes built-in to the animal function itself, which
is animal.prototype. So at this point there is three things that
we know. One is how to create a constructor function
and this is called a constructor function because it's constructing an object for us. The second thing is how to add methods to
the constructor functions prototype, and then the third is how to use object.create to delegate
failed lookup to the functions prototype. And all of this is for the purpose of sharing
methods across all instances of a particular constructor function. Now, it seems like there should be an easier
kind of like baked in solution to handle this. What we're doing here isn't that uncommon,
we're just wanting to share methods across all instances of a function, that should be
pretty fundamental to any programming language. And as you can probably guess, there is an
easier way or a more built-in way of handling what we're doing here. So looking back at our animal constructor
function right here, the two most important lines are this line right here and this line
right here. Without this line, we obviously wouldn't ever
create the object itself, and we wouldn't delegate to the animals prototype on failed
lookups. And without this line we would never actually
get the object that we created. So in JavaScript, you may have heard of the
new keyword before. The way it's used is like this, so when you
invoke a function, you put the new keyword in front of it. So now it's as if we are creating a new instance
of animal, so now the question becomes what's the difference between just doing something
like this, just invoking a function as you normally would, and including the new keyword
in front of that function. Here's the cool part. So if we…let's actually go ahead and do
this, so I'll say animal with new. If you use the new keyword in front of a function
invocation, JavaScript automatically behind the scenes implicitly some would say is going
to do this line for you, and it's going to do this line for you. The difference is instead of calling this
animal JavaScript is going to call this, this, literally T-H-I-S. So now any properties that we want to add
to the object, we use the this object. So again, whenever you use the new keyword
in front of a function invocation, behind the scenes, JavaScript is going to create
an object called this, which is going to delegate to the animal's prototype. And in this case it's going to be AnimalWithNew,
and then it's also going to implicitly return that object that it is creating for you. So we can see this in action, let's go ahead
and move all of animals prototype methods up here, and then I'm going to copy all of
those and change this to AnimalWithNew. And so now what we'll see when we run all
of this, Leo and Snoop, both have a name energy. LeoWithNew and SnoopWithNew, both have a name
and energy property. Leo can play, LeoWithNew can also play. So what we've done is by using again the new
keyword in front of a function invocation that tells JavaScript to create an object
called this, which is going to delegate on failed lookups to the functions prototype,
and then it's going to implicitly return it. So everything that we had to do before to
get to this stage right here, JavaScript will do all of that for you just by including the
new keyword. And so we can see how clean this looks by
getting rid of these under the hood comments here. And so there we go, here, let me move this
up so we can compare them. This is what you would do if you wanted to
not use the new keyword, right, so you'd have to use object.create to have all of the instances
delegate to animal.prototype on failed lookups. But if you wanted to use the new keyword,
you are fine with that, than what you could do is just add the different properties to
this. And then this line as well as this line will
automatically happen for you by JavaScript itself. If you're coming from a different programming
language like Java, really any programming language, you might be a little frustrated
right now. Because what I did was I essentially just
created a crappy version of a class, right? A class is really kind of just like a function
that returns you an object, and you can create different instances of that class. Well, this is the exact same thing we're doing
except we're using a function. Well, what's nice about JavaScript is, it's
a living language, meaning, it's always changing. And for the longest time JavaScript didn't
have a built-in way to create classes so what you had to do was you had to use this. You had to do something like this, right,
but now because as of ES6, which came out I believe in 2015, JavaScript now has a class
keyword. So what we can do, let me get rid of our animal
one here, so we just have AnimalWithNew, and I'm actually going to change this to just
be animal. Then down here, we need to make sure that
we call the new keyword, use the new keyword, right? So now if we wanted to recreate this using
an ES6 class, we can do that like this. Let's do that over here, so we say class animal,
the constructor method is what's going to basically like create that object. It's going to be similar to this function
right here. So we can take in a name and energy, and then
just as we did before we will say this.name equals name, this.energy equals energy. And then now instead of having to worry about
adding properties or adding methods to the prototype, what we can do, is we come in here
just do something like this. And if you're coming from another programming
language, this is going to feel really comfortable. So we have eat, sleep, and play. And then we can copy this logic into there. So, we have eat, we have sleep, and finally
we are going to have play. Play takes in length, sleep also takes in
length, and then eat takes in the amount. And then now same thing, in order to create
a new instance of our class, we just do as we usually word, and use the new keyword. So we can copy this now and I could show you
this working in the browser. So we have Leo, we have Snoop, and just as
we had before, Leo can play, Leo can eat, Leo can sleep, Leo can do any of the things
that are on the specific class. So you may have the question if this is the
new way to create classes in JavaScript, why did we spend so much time going over the old
way with prototypes, with this, and with a new keyword? Well, the reason for that is because the new
way, this way using the class keyword is primarily just syntactical sugar over the existing way
that we saw earlier. So to really fully understand what's happening
with the class keyword, you have to first understand what's happening with what this
gets compiled down to which is really just function in locations with the new keyword,
and then adding shared methods on a functions prototype. So at this point we've covered the fundamentals
of JavaScript's prototype. The rest of this video is going to be dedicated
to understanding other kinda like good to know topics related to prototype. Let's go ahead and do that now. When you create a new array in JavaScript,
you're probably used to doing something like this, right? Well, this way of creating an array is really
just syntactical sugar over creating a new instance of array, right, it would be kind
of a hassle to have to write out this every time. So JavaScript allows you to create an array
like this. Well, one thing that you might have never
thought about is how does every instance of an array have all those built-in methods,
things like slice, splice, pop, push. Well, now you know it's because all of those
methods live on the array's prototype. So we can see this by logging or just by showing
array.prototype. So you'll notice here that all of the array
methods live on the array's prototype, and then whenever you create a new array, you
create a new instance of array. So this is really just like our animal class
that we saw earlier, but instead of creating a new instance of animal we are creating a
new instance of array. And because we are using the new keyword,
friends will now be able to have access to all of these methods that live on the array's
prototype. So next, let's look at how we would get the
prototype of an object, right? So say we had…let's go back to the code
we had earlier, I'm gonna copy and paste all of this. And let's say for whatever reason we wanted
to get the prototype of Leo, right, we wanted to get the prototype that Leo will delegate
to on failed lookups is a better way to say that. So what we can do is we can use Object.getPrototypeOf,
and you pass in the specific instance, and then now prototype, it's going to have all
of those methods on it. So if you ever want to for whatever reason,
get the prototype that a specific instance delegates to, you can use getPrototypeOf. So just you can see this, prototype should
equal animal.prototype and it does. So now what I wanna show is how we can loop
over every key inside of Leo and log this to the console. So one way you might approach this is by using
a for in loop, so we can say for key, say let key in Leo. Let's just console.log, say key, and then
we'll say value. Don't fast forward, I promise we're gonna
get to something new here in a second. All right, so what we expect to happen here
is we want to log every key inside of Leo, so we expect to see name and then whatever
Leo's name is which is obviously Leo, and then energy and then Leo's energy. But you'll notice here we have name, we have
energy, and then we have eat, and then we have sleep, and then we have play. What we're doing accidentally is instead of
just logging all of the properties on Leo, we are logging all of the properties on Leo
as well as all of the methods on the object that Leo delegates to. So why is that happening? A for in loop is going to loop over all of
the enumerable properties on both the object itself as well as the prototype it delegates
to. Because by default any property you add to
the functions prototype is enumerable. Because by default any property you add to
the functions prototype is enumerable, we see not only name and energy but we also see
all the methods on the prototype eat, sleep, and play. So to fix this, we either need to specify
that all of the prototype methods are non-enumerable or/and I think a better way. We need a way to only console.log if the property
is on the Leo object itself, and not on the prototype that Leo delegates to on failed
lookups. So to do this what we can use, is the hasOwnProperty
methods. So what this looks like is we can say leo.hasOwnProperty. If we pass a name that's going to give us
true, but if we pass any property that lives on the prototype, like sleep, that's going
to give us false. As a quick side note, if we look at the object's
property the reason that Leo has access to hasOwnProperty is because hasOwnProperty lives
on the objects prototype, right here. Okay, so let's change up our for in loop real
quick. So we'll say if leo.hasOwnProperty key then
we want to come in here and console.log this. So now instead of logging all of the prototype
properties as well, we get just name as well as energy. The next trick I want to show you about is
how to check if an object is a specific instance of a class? So we already have our animal class up here,
let's create another class, just call it…let's say user so it's just a function for now. So we have an instance of Leo, so we can say
Leo instanceof, whatever the class name is, in this case we can use animal, and this should
be all lowercase for whatever reason. That's gonna give us true because Leo is an
instance of animal. And if we did Leo instanceof user, that'll
give us false, because obviously Leo is not an instance of user. All right, so the very last thing I want to
talk about is arrow functions. Arrow functions don't have their own this
keyword. As a result arrow functions can't be constructor
functions, and if you try to invoke an arrow function with a new keyword it'll throw an
error. So let's pretend we tried to create our animal
constructor function using an arrow function. So if we try to create an instance of animal
now using the new keyword, what's gonna happen is we get this error, saying, "Animal is not
a constructor." And even more than this because we just demonstrated
that we can't use the new keyword on arrow functions, there is no purpose of arrow functions
having a prototype property. So if we come in here and try to say animal.prototype,
notice we get undefined. So again you can't use a constructor function
or you can't use a new keyword on an arrow function. And because of that arrow functions don't
have a prototype property.