JavaScript Objects and Prototypes

  1. Using Object Literals to Create JavaScript Objects Okay, let's get started with our first demo. For all of our demos, we're going to be using strict mode. It's always a good idea to use strict mode. Strict mode causes JavaScript to throw errors in places it would, otherwise, just silently fail. And using strict mode disallows the use of deprecated parts of JavaScript. For this demo, we're just going to look at one of the simplest and most common ways to create objects in JavaScript--the object literal. To create an object with an object literal, you simply define the properties and their values inside braces like this. So here I've created a new object with two properties-- name and color. Notice if I call our display function and print out that it prints out Fluffy. You can see why this is one of the easiest and most commonly used ways to create objects.

  2. The Dynamic Nature of JavaScript If you come from a background in statically typed languages, you might be wondering what exactly we just did here. What is cat? We haven't even defined a class. That's the beauty of working in a dynamically typed language like JavaScript. You don't need all the ceremony of creating types ahead of times so that you can use them in your code. When you want an object, you just create it. Of course there are times when you want something that behaves more like classes in a statically typed language. And you can achieve a lot of the same behavior in JavaScript. We'll get to that shortly. But first, let's explore a little bit more about objects and the dynamic nature of JavaScript. One of the cool things about dynamic languages like JavaScript is that you can completely change the shapes of objects after they're created. Let's say we now want to add an age property to our cat. No need to go back and edit our object literal. We can just do it like this. And if we display that, you can see our cat is now 3 years old. No need to go changing our data types. No need to recompile. Just change it right where you need it. That's really powerful. Now I can hear what you might be saying in the back of your mind or perhaps yelling at the screen--How can you write an entire system like that? Sometimes I need a cat to look like a cat, and I need to be able to rely on the fact that a cat will always have a certain shape. Well that is very true, and we can certainly do that with JavaScript. We'll get to that in a second. Before we do, let's take a quick look at functions and how you might add a function to an object. Adding functions to an object is almost as easy as adding properties. You just do it. Let's give our cat a voice. We'll add a speak function to our cat like this. You can see I'm calling the display function inside that function. You could easily do an alert there or a console log or whatever makes sense in your application. For this demo, I just want to call the display function and print Meeooow on the page. So let's just get rid of these display calls and call our speak function. There, so you can see I created my cat. And after creating it, I added a speak function to it. And then when I called speak, it executed the speak function on my cat and displayed Meeooow. Of course, I could've just as easily added that as part of my object literal like this. And there you can see that works just the same. Our cat speak function executed and still displayed Meeooow. So you can see how JavaScript allows you a lot more freedom to work dynamically with objects. But let's take a look now at how you can get some of the same benefits you can from a statically typed language.

  3. Using Constructor Functions to Create JavaScript Objects Okay, so what if we actually want to create something that allows us to create multiple instances of cats, all with the same object structure, like a class in a statically typed language? JavaScript doesn't really have classes in the same sense as a static language. The new ECMAScript 6 spec does have a class-like syntax, but they're still not classes like you have in a static language. They do allow you, however, to accomplish the same thing in JavaScript without losing the dynamic nature of objects. This is made easier due to the new keyword. The new keyword is followed by a function that you create to initialize the object, like this. So let's create that function now. Notice that this is just a simple function. There's nothing special about that function. Notice that it is just creating properties on the object represented by this and sets the value of those properties. More on that in a second. First, let's just take a look at our new Cat object. There you can see that our lowercase cat variable is now a pointer to a Cat object, and that cat is named Fluffy and is white. So how exactly did that work? To really understand what is happening here, it's important that you understand what the keyword this is in JavaScript. The this keyword refers to an object. That object is whatever object is executing the current bit of code. By default, that is the global object. In a web browser, that is the window object. So when we executed this Cat function, what was 'this' referring to? It was referring to a new empty object. That's what the new keyword does for us. It creates a new empty JavaScript object, sets the context of this to that new object, and then calls the Cat function. Just to demonstrate that a little further, let's call that Cat function directly without the new keyword. Before we do that, I'm going to comment out this line, and I'll explain why in just a second. Now let's get rid of the new keyword, and we're going to have to get rid of use strict up here in order to demonstrate this. Now notice when we display our cat, it displays undefined. That's because we set cat equal to the return value of our Cat function, but our Cat function doesn't return anything. Previously, it was the new keyword that was returning our new object. But you can see that we called our Cat function, and the Cat function set the color to white on something. What did it do that to? Remember that without the new keyword setting the context of this, this is going to refer to the window object. So let's take a look at window.color. There it is. So you can see that this Cat function really isn't anything special. It's just a function. And it's going to set the values on whatever object is represented by this. If we put the new keyword back, we'll get our cat back. So why did I comment out this name? That's because changing the name property of a window messes with the browser. So without the new keyword, it would've changed the name of the window and freaked out Plunker. We can put that back now, and let's put our use strict back. So our Cat function's back to normal now. One last thing, however. We don't want every cat to be a white cat named Fluffy. So let's pass those values in. Now we have a function that we can use to create any type of cat. So let's pass in our name and color. There we go. So these functions, like this Cat function, are commonly called constructor functions. But you can see that there's really nothing special about them. They're just functions. But this is a very common pattern for creating objects in JavaScript.

  4. Using Object.create() to Create JavaScript Objects So far we've looked at two different ways of creating objects--using object literals and using constructor functions with the new keyword. It's worth noting that these are basically just syntactic sugar for object.create. We could create these same objects using the object.create syntax as follows. Let's go ahead and comment this out. And in place of this, we'll use this syntax. You can see here that we're using the object.create function to create our new object. And we're passing in the object that will become the prototype for our new object. And then down here, you can see that we're creating the name and color properties. For each of those properties, you can see that we're assigning the value and setting the enumerable, writable, and configurable properties to true. This all happens for us when using either object literals or constructor functions. Now aren't you glad that you don't have to do all that every time you create an object? The constructor function approach actually does a little bit more than this with regards to the prototype and the constructor. But essentially this is what's happening in both cases with respect to creating the objects. You can see that it's much nicer to be able to use object literals or constructor functions instead of having to do all of this. You may be wondering what those enumerable, writable, and configurable attributes are. We'll talk about that in the next module about properties. But before we jump into that, let's take a look at ES6 classes, another way to create objects.

  5. Using ECMAScript 6 Classes to Create JavaScript Objects For browsers that support it, the ECMAScript 6 specs now provide functionality for creating objects using a class structure that is similar to the class structure you might see in a statically typed language. Again, this is just syntactic sugar on top of existing object creation functionality, but it may feel much more comfortable to you if you're used to this type of structure. So to create our cat using a class, we would do something like this. Okay now you can see that this is very similar to using a constructor function, but it looks a little bit more like a traditional class. We can also add methods to our classes like this. Now I can call that method. There you go. So you can see this looks a little bit more like class in a statically typed language, only it looks a little bit more JavaScript-y. Really behind the scenes, these ES6 classes are just creating objects in the same way as the other methods we've looked at. But it may be a syntax you're more familiar with. You can also extend or inherit from other ES6 classes. But we'll talk more about that in the module on prototypal inheritance.

  6. Summary So here's a quick summary of what we've learned in this module. First, we learned about how to create objects using object literals, one of the easiest and most common ways of creating objects in JavaScript. Then we learned about constructor functions and how to create objects using those. And then we learned about the more raw object.create function, which is used behind the scenes by both of these approaches. And, finally, we learned about how to create objects using ES6 classes. In the next module, we'll learn more about object properties and the attributes that make up those properties. We'll also take a look at more advanced attributes of properties like getters and setters and how you can use these to create more advanced and powerful JavaScript objects.

  7. JavaScript Object Properties Introduction to JavaScript Object Properties Hi! This is Jim Cooper. Welcome to this module on JavaScript Properties. Since we just talked about objects and how to create properties on objects, you might be wondering how we could possibly have a whole module dedicated to properties, but it turns out there really is a lot more to properties in JavaScript than meets the eye, and there're some pretty cool things that you can do with properties if you aware of them. In this module, you'll learn how to do some advanced work with properties including working with property attributes and using getters and setters. Let's take a look.

  8. Using Bracket Notation to Access JavaScript Properties In the next couple of examples, you'll see me using the bracket notation for properties. This can be very useful in a few cases. And here's how you use it. First, notice that we can look at the cat's color property as we have been with dot notation like this. But we can also use bracket notation like this. Notice those produce identical results. So why would you ever use bracket notation? Well, what if you for some reason needed to create a property on an object using a property name that is not a valid identifier? For example, let's create a property on our cat called Eye Color. We'd do that like this. Now let's take a look at that value. You may still be wondering why you might ever do this. Well, what if you wanted to create an object out of values being entered by a user? Or it's possible you have a source of JSON data that has property names that are not valid identifiers. It's not too common that you run into this, and it's best to use valid identifiers for your property names for simplicity, but it is nice in the rare cases that you need it.

  9. Using JavaScript Property Descriptors Now let's take a closer look at properties. You may be surprised to learn that a property is more than just a name and a value. Every property has a property descriptor that we can use to see the attributes of that property. To demonstrate this, notice that I changed my cat object back to a simple object literal for simplicity's sake. So let's take a look at the property descriptor for the name property of our cat object. We can do that like this. So we're printing out the property descriptor for our name property. And you can see that in addition to the name property having a value, it also has writable, enumerable, and configurable attributes. And these are all set to true by default. In the next view clips, we'll take a look at each of these attributes and what they're used for and how you can change them.

  10. Using the Writable Attribute The writable attribute does what you would probably expect--it defines whether the property's value can be changed from its initial value. So let's make the name property non-writable. We can change the property attributes using the Object.defineProperty method like this. There, now you can see that the name property is not writable. So let's see what happens if we try to change the name of our cat. First, I'll open the error console so you can see any errors. Now let's change the name of our cat. You can see that I've thrown an error because my property is not writable. Now it's very important to know that this behavior of throwing an error only occurs in strict mode. Notice that if I clear my console errors and then go up and remove 'use strict' that I don't get any errors. That's kind of scary. One of many reasons why it's best to always run in strict mode because, in this case, you would think that you're assigning the value Scratchy to your cat's name, and it would just silently fail and you would have no idea. So it's always best to run in strict mode. So we'll go ahead and put that back. Now let's take a look at what happens if a non-writable property contains an object. So let's make the value of our name an object, and that object will have a first and a last name like this. So now my name property's set to this object, and I'm trying to set it to Scratchy here still, and it's failing and throwing errors. That's why we're not getting a display. But here's the interesting thing. I actually can change the object that is pointed to by that property like this. Now you can see we're not throwing an error anymore because our display is working. Let's go ahead and display the cat name. So there you can see that even though the name property was read-only, I was able to change the value of a property of the object that the name property was pointing to. This makes sense when you think about it since name is really just a pointer. And when you make it read-only, you're only preventing that pointer from being changed. As a side note, you actually can prevent the object from being changed by using object.freeze like this. And now that that's frozen, you can see that I'm getting an error because the entire name object is now read-only in addition to the actual name property. So just keep that in mind if you ever make a property read-only that if the property contains an object, you'll need to freeze the object also in order to prevent it from being changed.

  11. Using the Enumerable Attribute Okay, now let's take a look at the enumerable attribute. Before we look at that, though, let's take a look at the for�in loop. You can use the for�in loop to loop over each of the properties in any object like this. Okay, so you can see here that we're looping over each property in the cat object, and each time through the loop, it returns the name of the property and assigns that to our property name variable. And then we're displaying each one of those. Now we can use those property names to get the value of each property using the bracket notation we talked about earlier like this. So now we're displaying both the name of the property and the value of the property using bracket notation. You can see that the name property is set to an object and that the color property is set to white. Now back to the enumerable attribute. By default, properties on an object are enumerable, meaning we can loop over them using a for�in loop. But we can change that. Let's make enumerable false for the name property. There, now notice that even though I'm still looping over all the properties in my cat object that the name property was not actually returned in my loop. That is one of the main use cases for the enumerable property. Setting enumerable to false also makes it so the property does not show up in the object keys. You can look at an object's keys like this. Now let's just display that. Notice that object.keys returned an array with all the properties except notice that name is not showing up because it's not enumerable. If we make name enumerable again, then you can see that it does show up in the object keys. And, lastly, setting enumerable to false affects JSON serialization of the object. Notice if I serialize my cat object to JSON that I can get the whole object. I'll have to scroll over for you to see all of that. So there you can see our color property and our name property both got output in the JSON. But if I change my name property to not be enumerable again, then you can see that that property is not JSON serialized. So that shows you the power of the enumerable property. Note that you can still look at the property with bracket notation. So setting enumerable to false does not change the ability to look at the property. You just can't enumerate it, see it in the object's keys, or serialize it.

  12. Using the Configurable Attribute The configurable property locks down a property to prevent certain attributes from being changed. It also prevents the property from being deleted from the object. Let's take a look at that. Say we want to lock down the cat's name property so that its attributes can't be changed. We'd do that like this. Now notice that if I try to change the property's enumerable property that I get an error. I'll open the error console first so that we can see the errors. There, so you can see that I get an error if I try to change the enumerable property indicating that I cannot redefine the property. It is interesting that once you have made a property non-configurable, you can't make it configurable again. Notice if I try to change this that I get a second error. You can see that with the #2 here. So this threw another error. You can, however, still change the writable attribute. You can see that did not throw another error. There is one more thing that changing the configurable property does. It makes it so that you can't delete a property. So notice that if I try to delete the name property, that I get this error Cannot delete property name of object. And if I get rid of this so that the property is still configurable, you can see I don't get an error. And if I display, it's undefined because I've deleted that property. So to recap, there are three things that are affected by making a property nonconfigurable. If configurable is false for a property, you cannot change the enumerable attribute, you cannot change the configurable attribute, and you cannot delete the property. You can, however, change the property's writable attribute. cool

  13. Using Getters and Setters Getters and Setters are a couple of pretty cool attributes on a property that allow you to specify the return value of a property using a function and set the value of a property using a function. But you can access the property just like you would any other property. Let's take a look at how you might do that. Our cat has a name property, and that name property has an object with a first and last name. What if we want to know the full name of the cat? Let's use a property getter to create a full name property that does that. To create getters and setters, you have to use defineProperty like this. Now we have a full name property that will return the first and last name appended. Let's take a look at the full name of our cat now. Now you can see that it's displaying the full name of our cat, and we were able to access it just like we would any other property even though it's actually executing a function behind the scenes. That's pretty cool. Now what if we wanted to be able to set the first and last name based off of setting the full name? Well, let's add a setter for that. Now we can set the cat's first and last name like this. And you can see that the full name is now Muffin Top. But not only that, if we take a look at the first and last names, you can see that those are also correctly set. So here we're setting the full name as if its a property, and yet behind the scenes, it's executing a function and doing a lot more than that. So you can see that you can do some pretty cool stuff with JavaScript objects. And there really is a lot more to JavaScript properties than initially meets the eye.

  14. Summary In this module, you learned about the writable, enumerable, and configurable property attributes. We also looked at how to use property Getters and Setters to create powerful properties backed by functions. In the next module, you'll learn about prototypes, which will allow you to use them in your code to inherit from or extend functionality in other objects and to recognize when they're being used in powerful frameworks like React and Angular.

  15. JavaScript Prototypes and Inheritance Introduction to Prototypal Inheritance Hi! This is Jim Cooper, and welcome to this module on JavaScript Prototypes and Inheritance. If you've heard about prototypes but haven't really ever used them or perhaps stumbled across some JavaScript code that used prototypal inheritance and that left you scratching your head, this module will give you a solid understanding of how prototypes work and everything that is going on behind the scenes. Once you understand prototypes, you'll be able to write simple yet powerful code that, otherwise, would have eluded you. This understanding will also allow you to recognize when prototypal inheritance is used in frameworks like Angular or React. And that should help you make better decisions when using those and other frameworks. So let's jump right in.

  16. Getting Started with JavaScript Prototypes To get started with prototypes, let's use a simple and common example of when you might want to use a prototype. Let's create a simple array like this. Now imagine if we wanted to get the last element of the array. Typically you'd do that like this. And you can see that retrieve the last argument if we display that. There we go. But what if you wanted to simplify that by extending the array object so that you could just ask for the last element in the array like this? JavaScript arrays don't have a last property, so this won't work. But, of course, JavaScript is dynamic, so we can add our own last property. I'll do that with defineProperty so we can call it like a property instead of a method. Now you can see that array.last works. And we're displaying green over here, which is the last element in our array. But we have a problem. Notice when I create a new array, this array does not have a last property. So if I try to access it, you can see that it's undefined. What we need to do is put this on the array object's prototype. We can do that changing this to Array.prototype. Now if we go down here and display our first array again also, then you can see that both of our arrays have a last property because they're both getting displayed here now. So what is this array object? If we display it, you can see that it's just a function. It's a function that's meant to be used as a constructor function. This array syntax here is really just shorthand for calling the array constructor with new like this. You can see that works just the same. So what is Array.prototype? Let's take a closer look at what prototypes are.

  17. What Is a Prototype? So what is a prototype? A prototype is an object that exists on every function in JavaScript. So notice if I create a new function like this, notice that it has a prototype property. Also notice that prototype property is just an empty object. Objects, however, do not have a prototype property. So if I create a new object and try to view its prototype property, that is undefined. An object does have a proto property, however. Let's take a look at that. So an object really does have a prototype assigned to it. But an object's prototype and a function's prototype are used differently. So let's provide a couple of definitions that we can work with going forward. These might not make a lot of sense now, but they will once we provide a few examples. So here are a couple of definitions. A function's prototype is the object instance that will become the prototype for all objects created using this function as a constructor. An object's prototype is the object instance from which the object is inherited. Again, don't worry about these definitions too much right now. We'll show a few examples, and the should make a lot more sense next time we display them. It is important, though, to take note of the word instance in those definitions. A prototype is not like a class. It is actually an object. So when a function is created, it gets a prototype object created and attached to it behind the scenes. If that function is then used as a constructor function with the new keyword, the object that is created has a proto property that is pointing to the same object that is the function's prototype. This is much easier to understand with code, so let's take a look. For example, let's use our Cat function. To start with, let's look at its prototype. So you can see here that our function has a prototype. Now let's create a new cat from that constructor function. And let's look at Fluffy's prototype. You can see that they both have the same shape. However, they're not just the same shape. They're pointing to the exact same instance of an object. Notice that they are the same instance. You can see that they're equal. This would only return true if these two objects are actually the very same instance. You can see that further if I change one of them. Let's change the Cat function's prototype. And let's print them out again now. So there you can see that while I only change the prototype for the Cat function, it is also reflected in Fluffy's prototype since they're the same object. The same holds true if I create another instance of cat. You can see that both instances of cat are pointing to the same prototype instance. Next, let's take a deeper look at what's actually happening with that age property on our prototype. And then I'll graphically illustrate everything that's happening behind the scenes. After the next couple of clips, I promise this'll all make a lot more sense, and we'll revisit our definitions.

  18. Instance vs. Prototype Properties Okay, so we've seen how adding a property to a function's prototype affects all objects constructed using that function. But let's take a closer look at what exactly is happening behind the scenes. Specifically, let's look at exactly where those properties live. Here we have our Cat function, and two cats derived from that function. And notice that the Cat function's prototype has an age property that is set to 3. That means that each of our cats have an age of 3. And we know if I change Cat.prototype.age, that will change the age for all cats derived from this function. But what happens if instead of changing the prototype's age, I change the age of just one of the cats? Although it makes sense looking at the code, it's interesting that in this case, only Fluffy's age was changed. So what exactly is going on here? If the age property is coming from the prototype, how is it that I can change one without changing the other? Well, it's because we didn't actually change the prototype's age property. What we really did here was add a new property to the Fluffy object. Notice that Fluffy still has access to both values. So you can see that Fluffy has an age of 5, but Fluffy's prototype has an age of 4. The key concept to notice here is that prior to setting Fluffy's age directly, the Fluffy object never really had an age property. Only its prototype did. But if that's the case, why is it that if I get rid of this age property on Fluffy, we can still ask for the age property on Fluffy and get a value back? Notice here that I'm accessing fluffy.age, and it's displaying 4 even though Fluffy doesn't actually have an age property. We can further see that Fluffy doesn't have an age property by displaying its keys. You can see here that it only has a name and a color property. This is because what JavaScript is really doing when we ask for the property value is it looks at an object to see if it has a value for that property name. And if not, then it asks its prototype. We can see this further by using the hasOwnProperty method like this. You can see here that JavaScript says that Fluffy does not have its own age property even though we can ask it for an age property and get a value back. Notice if I change this to color that it returns true because Fluffy does have a color property. So if we change this back to age, and then I go and actually give Fluffy an age, then you can see that it returns 5 for the age, and JavaScript indicates that Fluffy does have its own age property. So that's important to understand that if you ask an object for the value of one of its properties, just because it gives you an answer doesn't mean that the object itself has that property. It may be one of the prototypes in its prototype chain that is actually returning that value. And by the way, keep in mind also that everything we've talked about with properties, including the way things are inherited, is true of functions on an object also because functions really are just another property. So if my Cat prototype had a speak function, it would behave in exactly the same way that the age property does with regards to prototypes and inheritance.

  19. A Graphical Overview of Prototypes So let's take a look at this in another way to help understand exactly what's happening here. First of all, when we first created our Cat function, JavaScript also created another object in memory. This is essentially an empty object at this point although it does have a proto property that we'll talk about later. But for now, consider this just an empty object. After creating this object, JavaScript updates the function's prototype property to point to this new object. And we can manipulate the function's prototype object like this. When we add the age to the Cat's prototype, it adds that property to this object. Remember, we haven't even created a Cat object from our function yet. This object is just the hidden prototype object that JavaScript created behind the scenes. So let's create a new Cat object from our function now. We do that like this. When we do that, the new keyword takes care of creating a new object. But the new function also does something else behind the scenes. It adds a proto property to our new object. And that property is a pointer to our Cat function's prototype. Then the new keyword executes our Cat function, and the this keyword in this context is pointing to our new Fluffy object. So it adds the name and color properties to our new object. And if we create another instance of a cat, the same process is followed. A new object is created. Its proto property is pointed to the function's prototype, and the instance properties are added. Notice that at this point, the Fluffy object itself does not have an age property, only its prototype does. So if we ask for Fluffy's age at this point, what's going to happen? Let's take a look. So I'm going to request Fluffy's age in the code like this. When I do that, JavaScript will check the Fluffy object to see if it has an age property. Since it does not have an age property, then it will check its prototype parent to see if it has an age property, at which point it discovers that it does, and it returns a value of 3. So this console log would log 3. So now what happens if we add an age property to Fluffy? We simply do that by assigning it a value like this. That adds an age property to our Fluffy instance here. Now if we ask for Fluffy's age, it will check to see if Fluffy has an age, and it does, so it returns the value of 5 without ever checking to see if the prototype has an age property. So the instance properties override the prototype properties. And this console log statement ends up writing out the value of 5. So what would happen if we updated the Cat function's prototype's age property? We could do that in two ways, either directly like this, or we could do it through Fluffy's proto property like this. As our code stands now, this would have the exact same effect. It is possible, however, for these two statements to return different values. We'll talk more about that later on. For now, let's just stick with the more direct approach. When this line of code executes, it will change the age property of our function's prototype here. Now what would we get if we ask for Fluffy's age? We would still get 5 because the instance property overrides the prototype property. But what if we ask for Muffin's age? We would get 4 because Muffin has no instance property for age. So with all this in mind, let's just revisit the definitions we created earlier. A function's prototype is the object instance that will become the prototype for all objects created using this function as a constructor, whereas an object's prototype is the object instance from which the object is inherited. Hopefully, these definitions make a little more sense now. Prototypes are really not that complex once you understand what's happening behind the scenes.

  20. Changing a Function's Prototype In the definition of a function's prototype, we said the function's prototype is the object instance that will become the prototype for all objects created using this function. Let's take a look at a quick example that illustrates that. Here we have our Cat function and two cats, Fluffy and Muffin, which were derived from that function. If we look at their ages, you can see that they're both inheriting an age of 4 from the Cat function's prototype. We know that if we change the age of the prototype, that that will also change the age of Fluffy and Muffin through inheritance. But what if we actually change the function's prototype to point to a completely different object. Let's try that. Notice that that did not change the age of our two cats. And if we look at the Cat function's prototype, we can see that it does have an age of 5. Notice also that if I create a new cat after having changed the function's prototype, notice that Snowbell has an age of 5. So what exactly is going on here? This really highlights that the Fluffy and Muffin objects have prototypes that are pointing to an instance of an object in memory. Let's take a look at what really happened. Here we have our diagram from earlier showing our Cat function and our two instances of cats. And you can see that they are all pointing to the same prototype in memory. When we changed the prototype of our function, what we really did was create a new object in memory and changed the function's prototype property to point at that new object. However, the existing Fluffy and Muffin instances of our cat still have prototypes that are pointing to the old prototype object. When we then created our new Snowbell instance of a cat, it created a new object and set its prototype to point to the current prototype of the Cat function. This really highlights the fact that prototypes really are objects that live in memory. And as you would expect, they behave like any other objects with regards to pointers.

  21. Multiple Levels of Inheritance So far we've only been talking about a single level of inheritance. But, actually, the objects we've already been working with have multiple levels of inheritance already. We know that Fluffy has a prototype and that it was created from our Cat function as we can see here. What we haven't seen is that the Cat prototype also has a prototype. Notice that it's prototype was created from the object constructor. So what if we keep looking up the chain? Notice that object's prototype is null. Eventually as you walk up the prototype chain, you always find a null prototype. And this is usually after hitting object's prototype. By default, all objects in JavaScript inherit from object. And object has no prototype. So almost all objects that we work with have some type of prototypal inheritance chain like this. Next, let's take a look at how we can create our own chains of inheritance using our own objects.

  22. Creating Your Own Prototypal Inheritance Chains Now let's take a look at creating our own prototypal inheritance chains. What if we wanted our cat to inherit properties from a parent called Animal? Let's take a look at how we'd do that. Let's make it so that all animals have a speak function. Now any function that uses Animal as its prototype will get the speak function automatically. So now we need to assign Animal as the prototype for the Cat function. We simply do that like this. So now we can make Fluffy speak. And you can see here that Fluffy grunted. The speak function is not a member of cat, so hasOwnProperty would return false. But it is a member of its prototype Animal. Now you might be wondering why we use object.create here instead of just using new. The big difference is that object.create is not going to call the Animal function. It's just going to set that function as the prototype and set up the prototype chain. If we called new, it would actually execute the Animal function. And we probably don't want to do that just while we're setting up our Cat function. We don't really want to call that until we're creating instances of cats. So that leads us to the next thing we need to do. We do need to call our Animal constructor from our Cat constructor. This way if anything needs to happen to initialize an Animal upon construction, it will be taken care of. We just do that like this. So that will call the Animal function passing in the cat being constructed. So any Animal-related initialization can occur. Let's just add something to demonstrate that. Let's make it so that our cat can meow instead of just grunting. To do that, let's have our Animal constructor take in a voice. And we'll update our speak function to use the Animal's voice. Now we can optionally pass in a voice. Let's do that from our Cat constructor. Now you can see that when we call the speak function, our cat will meow instead of grunt. There're a couple more things that we need to do in order to tie up some loose ends here. First of all, notice if we display Fluffy, notice it says that Fluffy was constructed from Animal. But Fluffy was actually constructed from Cat. First of all, let's understand where this is actually coming from. This actually is just something I'm doing in my display function. If we look at display.js, you can see right here that I'm displaying the type name of an object when we display an object. But there really isn't such a thing as a type name in JavaScript. So if you look at this type name function, what we're really doing is looking at the object's constructor function and parsing the function name out of that function definition. So we have the wrong constructor associated with Fluffy. It is worth noting that Fluffy actually is a cat according to JavaScript. We can see that with the instance of operator. So you can see that returned true. Fluffy is also an Animal. That returns true also. But the constructor is still wrong. This is because when we define the prototype for our cat here, it also set the constructor function of our cat to be Animal. We want our constructor to be the Cat function. So we can do that like this. So now if we display Fluffy, we can see that Fluffy is a cat again. And we can see that Fluffy's prototype is a cat and that Fluffy's prototype's prototype is an Animal. It seems like we've covered a lot of steps here. But, really, there are just three lines that you need to worry about when creating the prototype chain, and that is these three lines here. You just need to implement these three concepts whenever building a prototype chain. And that's all there is to it.

  23. Creating Prototypes with Classes If you prefer a more class-like structure to your code, you can build the same types of prototypal inheritance chain using a class-like syntax. Whether you use traditional constructor functions or classes, the end result is very similar. Everything you do with classes is mostly just syntactic sugar on top of everything you've learned about prototypes with some small differences. Let's take a look at how you'd accomplish the same thing using classes. It actually is a little cleaner. So, first, we'll create a class called Animal, and it'll have a constructor like this. So here's our Animal class, and you can see it has a constructor that takes in a voice and defaults its voice to grunt if no voice is passed in. And that replaces this code here. And then we'll add a speak method to our class. And that replaces this code here. Now let's create our Cat class, and it will extend from our Animal class like this. So that extends keyword is new, and so is super. Extend is what you use to set up your inheritance chain. And you can see that Cat still has a constructor function that sets the name and color, but you can see that it also calls its parent's class's constructor using the super keyword. So that will call Animal's constructor prior to setting the name and color of the cat. So that all replaces this code here. So now we have our cat that inherits from Animal. And we've created a new instance of our cat here. So let's see if Fluffy can speak. You can see that because Fluffy is a cat, when we called Speak, it displayed Meow. And if we look at Fluffy, you can see that Fluffy still has a voice, name, and color. There are a couple of minor differences that, really, you won't typically care about, but they are worth noting. Notice that my display function when we're displaying Fluffy is failing to indicate the type here. Remember, using the constructor function, this said Cat before. If we go look at my display function, that's just because the regex that I'm using in this function here is not an appropriate regex for classes. Notice here that I'm looking for something named function in the constructor. Here you can see that I'm looking up the constructor, and I'm using this regex to try to parse what the name of the function is. But in the case of classes, the constructor is not a function, it's a class. So if we look at fluffy.constructor, you can see it does still have the constructor, that that constructor is a class, whereas before it was a function. So that's really the only difference there is that the constructor is a class. And then there is one other difference to take note of when using the class syntax. Members of classes are not enumerable by default. So this speak function is not an enumerable property of the Animal class. That means if we look at the object.keys for Fluffy's Animal ancestor, notice that that is empty, even though if we look at hasOwnProperty for that speak function, that returns true. So while there is a speak function on Fluffy's Animal prototype, it is not enumerable, and so you don't see it in the object.keys, and you won't see it if you try to loop over the properties of the Animal class. So that is another minor difference between this and the constructor syntax. You won't typically run into these differences, however, so don't worry too much about them. But it is a difference. Other than these two minor and relatively unimportant differences, the class syntax works the same as constructor functions. Everything that you've learned about prototypes still applies to objects created using classes.

  24. Summary This concludes this course on JavaScript objects and prototypes. In this last module, you've learned how prototypal inheritance works. We also looked at the difference between instance properties and prototype properties. You learned about multiple levels of inheritance. And you learned how to create your own prototypal inheritance structures using both constructor functions and classes. I hope this course has been enlightening for you, and good luck with your JavaScript endeavors.