Advanced JS Fundamentals to jQuery & Pure DOM Scripting Prototypal Inheritance
Transcript from the "Prototypal Inheritance" Lesson
>> Alexis Abril: So let's take a little bit more complex example. Okay, if we wanna create new animals that's cool. I wanna create new animal, give it a name. But we probably want more abstracted layers, right? If you have a shape, maybe you want a parallelogram to inherit from shape.
[00:00:25] And then maybe you want square to inherit from parallelogram. That if you want many, many more constructs, many more layers, we can do that a little bit differently. Well, it's the same concept but more times. So here's an example inheritance ladder if you will. We have animal as our base construct.
[00:00:45] We have chordates, which will inherit from animal, and we have mammals which inherit from the chordates. This is the structure that we wanna create conceptually. We're gonna show you how to do that. So the differences between the three things, all animals eat, chordates are a subclass of animals.
[00:01:02] They will all have spines and mammals are subclass chordates. They will all have hair. What this is gonna do, it's gonna look something like this. We're gonna have our mammal function, which will have a prototype, pointing to this object in space right now, this new object. Chordate will have the same thing and so will animal.
[00:01:26] They will all have, they will be constructor functions with their respective prototypes. Now this doesn't set up any kind of link between them yet, we have to do that for us. The part that we care about is, each object's proto property should point to where we want to inherit from.
[00:01:44] If we don't set this part up, everybody's proto property would point to the base object. And we wouldn't really get any kind of cool inheritance. This is the chain we want to that way when something comes to a new mammal and we wanna say, hey I want you to eat something, it's gonna walk up the proto chain until it hits animals prototype and then invoke the eat function.
[00:02:33] It's not doing anything yet. It's just a new constructor function. And we're gonna say, prototype equals new animal. This is what's gonna link our prototype to animal. We've created a new object in memory with its proto property pointed to animals prototype. And they we're gonna point our proto pointer to that new object.
[00:02:58] It's gonna create this type of link for us. And then we're going to give ourselves a has spine property, which will just say true. And we're gonna do the same thing again for mammal. We're gonna create our constructor function. Have a prototype with a new chordate, this will link the two.
[00:03:15] And then we're gonna give it, it's own has hair equals true. So that way when we finally come along and say something like, m equals new mammal dog, cuz that's a really clever name for a mammal, is dog. We're gonna create a new object with its proto property.
[00:03:35] And m is gonna be able to have all the properties that we have. Has hair, has spine, and the eats function will all be accessible on m, because we've set up our inheritance tree.
>> Speaker 2: I'm a little confused why you have to use a prototype for eats, when you define eats up there.
[00:03:57] Wouldn't eats be inherited by chordate when you set it up?
>> Alexis Abril: Would eats be inherited by chordate. So how would you, how would you write this otherwise?
>> Speaker 2: Well if you just removed prototype from an Animal.prototype.eats
>> Alexis Abril: So you're saying animal.eat equals function, iss that what you are thinking?
>> Speaker 2: Right, when you set up chordate, would it automatically inherit eats or-
>> Alexis Abril: Let's think about that for a second. So here's our animal function, and if you wanna say animal.eats where would eats go? It would go right here, right? It'd be animal, and then a pointer to eats.
[00:04:34] But this is down on this function, so when chordates or anything else, tries to walk up the inheritance tree for eats, it's gonna go to each proto object but it's not going to go down to constructor function and look for things here. It's going to go here. This doesn't have eats.
[00:04:48] Go to the base object, it doesn't have eats. So it's gonna return that type error, method not found or undefined, because we've hidden it down here.
>> Speaker 2: Okay.
>> Alexis Abril: Now sometimes you do wanna have methods or properties on the function themselves, if you're building some kind of helper library, that you're using internally, but you have to have a very specific reason for this kind of stuff.
[00:05:07] This is more what we would consider a static method, not in the strict sense, but you can think of it conceptually as this is on instances of objects, instances of animals, and this is on animal, capital A, itself. Does that make sense? Cool, good question.
>> Alexis Abril: Okay now there are some problems with this.
[00:05:35] One of the problems you may have seen from this wall of text, is in the constructor we're doing the same thing over and over and over. We have to say this.name. There's a way around this. It's just not, the concert doesn't exist because it's not true inheritance, you can't have methods inheriting from other methods in the strict sense.
[00:06:00] This is when you see libraries providing things like super plugins. But it's not inherit in the language itself. But it would makes sense if you inherit chordate from animal, you probably want to invoke your own constructor logic, set up your spine. But you want to invoke animals constructor logic as well.
[00:06:20] You don't wanna have to rewrite that code every single time you're setting up something to inherit from. So you can do that this way. So here's our base animal but inside chordate, we don't wanna call, we don't wanna set up this.name equals name again, instead we just wanna do whatever our parent does.
[00:06:41] So here is a really really nice use of call and apply. Animal is just a function. So we are going to invoke the animal constructor. But we're gonna set its context to whatever we are, whatever this is. And remember, one of our rules for this is the new keyword.
[00:07:01] So when you say var c equals new chordate, we're setting the context within this function here to the new object. And we're passing that into our constructor above. So this.name will still be our new object.name. So the same functionality is achieved in the set context correctly, except we don't have to rewrite this.name equals name.
[00:07:32] Does that make sense? This is really useful because typically, you're gonna have a more elaborate constructor function than this.name equals name. You probably want to actually set up some things. But you don't want to re implement that same constructor logic over and over and over. Interjection?
>> Speaker 2: Why can't you just do chordate equals animal?
>> Alexis Abril: Why can't you do chordate equals animal? I want a whiteboard. [LAUGH] Why do you guys think you can't do chordate equals animal?
>> Alexis Abril: Or what repercussions would that have?
>> Speaker 3: [INAUDIBLE] Everything that Chordate has, right? Then you just have it pointed at animal?
>> Alexis Abril: Yeah. You'd be pointing to the same function.
[00:08:19] Remember, everything's a pointer. So in this case, if you had animal equals function, this.name equals name, chordate would just point, these should really be, if you wanna consider these functions and the animal points to a function here, chordate would point to the same function.
>> Speaker 3: So basically that would become a problem when you went to the next step of chordate.prototype.spine is true, then you've set spine true on all animals.
>> Alexis Abril: Yes.
>> Speaker 3: Got it.
>> Alexis Abril: Exactly.
>> Alexis Abril: Good question. Okay and then of course, doing the same, you can do the same thing for a mammal. You get down to mammal, say chordate.call. We're setting up the same inheritance link, but without having to reimplement our constructors every time.
[00:09:17] Okay, we'll talk about our shared properties example, our issue with shared properties really, really quick. So, we're gonna construct that function we saw earlier, but we're gonna do it in our little 2D representation here. So if Animal equals function. Remember, it's not going to evaluate the function. It's not going to execute it.
[00:09:39] It just knows that there is a function. So function, by default, all functions have prototypes, again. Not by default, they just do. So animal, and we have our prototype, which is gonna point to an object. We have dog equals functions, so we're gonna get the same construct in memory.
[00:09:56] And then when we say dog.prototype equals new Animal, we're gonna first, dog.prototype equals new animal, so dogs prototype object, its proto property is gonna point to animal's prototype, and we're going to invoke the animal constructor with the context of dog.prototype. This is our context here. Because we're using the new keyword, this newly created object is gonna be our context.
[00:10:28] So this dot offspring will be, this is our array object here. So far so good? Then, when we create our three new dogs, here's dog one, dog two, dog three, new objects with their proto property pointed to dogs prototype. Remember this is invoking the new key word here, for dog new dog, is invoking dogs constructor.
[00:11:01] And dog's constructor is not setting offspring to this, and it's not recalling animals constructor with its context. It's an empty function. So this is just creating new objects with proto properties pointed to dog's prototype. So when we invoke a dog.offspring.push pup, we're saying, dog1.offspring. Do you have offspring?
[00:11:33] No, walk up to protochain. Do you have offspring? Sure do. Here's the offspring array, push pup, so push this guy.
>> Alexis Abril: So when you say dog2.offspring. When you look this up later, you're gonna say dog2, do you have offspring? Nope. Walk up the proto chain. Do you have offspring?
[00:12:00] Yes. What is your offspring? It's gonna be an array containing pup.
>> Alexis Abril: So you're right, at face value just looking at the code, I wouldn't have expected this. But in memory, it makes a lot more sense when you're just drawing it out. Yes, okay, I see what's going on.
[00:12:23] Does this is make sense? Yeah, I really do draw a lot of this out still. So it's a very helpful tool.