Kyle Simpson: Now what about this linkage that we've been talking about? What happens if I call a1.identify? What's it going to do? Is there an identify function on a1?
Kyle Simpson: Well, so it's going to say a1., and it's going to traverse the prototype and it's going to say is there an identify on this object?
Yeah, beautifully there is. So here's the first benefit that we start to see of the prototype linkage. Is that we have this ability to delegate to a different object to handle a method call, or property reference. Notice that we have a1 and a2. There's not multiple copies of the identified function like there would be in the module pattern.
What happens if we created a thousand of these, a1 through a1000 and every one of them was linked to this one. There would only be one copy of the function. And that function we see right there on line five, it references a this keyword. When I say a1.identify what is the this binding going to be inside of that call?
Speaker 1: a1.
Kyle Simpson: a1 which is what we want it to be so it's going to get the mean that a1 has. If we say a2.identify what is this keyword going to be?
Speaker 2: a2.
Kyle Simpson: a2, exactly like we want. And the same for a3 through a1000. So though this keyword ends up being a really nice mechanism for us, when we're dealing with delegation of the prototype chain, only when we behave by the rules we'll see here in a moment on how it can get bad.
But, if we behave by the rules, that binding ends up working really well for us.
Kyle Simpson: Now what happens here on this I have a slightly different code. You see a1.identify on line 9. What happens if I then on line 11 do a1.identify = and I add a property?
Where does that change? Does it change this guy?
Kyle Simpson: No it actually adds it directly here.
Kyle Simpson: Which depending upon your mindset may or may not make much sense. But it didn't change that guy, even though a1.identify as an RHS reference would've delegated up and called that function.
When we say a1.identify, we're not replacing that guy, we're adding a new function directly to a1. So now when we call a1.identify on line 7, which one are we calling? This guy or this guy?
Kyle Simpson: We're calling this guy's instance, this is called shadowing. Remember I talked about variable shadowing in scopes?
Same thing can happen with prototype chain. Properties on the prototype chain can shadow each other. Now that I have a shadowed version of this identify, whenever I say a1.identify, it's always going to find this guy, it's never going to find that guy. Now here's where our things starts to break down.
Because class oriented coding, the design pattern of classes, tells you to create parent classes and child classes that have method names that are the same. And it says create an abstract version of a method on the parent class, and on your child class, create override that version of it and be able to do things like relative polymorphism where you call super to call one level up the chain.
That's one of the major benefits of the design pattern of classes, is the ability to name your things the same thing at multiple levels. But here, we see that it actually creates a whole world of problems. Because look what happens on line 13 if, inside of this guy, I did want to somehow reference that guy.
I can't say this.identify, because that's just going to recurse to myself. How do I do it? I do this crazy thing that I call, sort of explicit polymorphism. I have to say foo.prototype.identify. So to manually find this method. And not only that, I have to say call this. Because I want to make sure that when that guy gets called, a1 is the this binding.
You're asking for having to do crap like that. If however, we had called this guy identify two or anything else except for identify, then inside of that function, we could have said this.identify, and it would have delegated up to that guy. So if we use different method names, everything works beautifully, let me show you that.
Next slide shows us what I call super unicorn magic. Because as you'll see here, I have a foo prototype identifier, and a foo prototype speak. On line 16, when I call a1.speak, speak gets called through the delegation on foo prototype. But what does speak do. He says this.identify.
What is the this key word inside of speak?
Speaker 2: a1.
Kyle Simpson: a1, so it's like saying a1.identify. Does a1 have an identify?
Speaker 3: No.
Kyle Simpson: No. But he steps right back up the prototype chain. So the nice thing about the way that this mechanism works is it's always rooted at that call sign.
The this keyword always references the call site. And that's always what we want unless we do that crappy shadowing thing. So the takeaway that I would give you, just at this point until you understand what I'm going to suggest as the alternative, the takeaway that I would give you is, it's your choice whether or not you do shadowing.
And whether or not you design your software to expect the same method name at multiple levels. If you avoid shadowing, everything works with super unicorn magic, it's just always the right thing. If you decide to do shadowing, good luck with all that extra crappy syntax, okay? Yeah.
Speaker 4: And the end result is that you want to have it delegate a function that can be shared across multiple objects.
Kyle Simpson: Maybe, yeah. Not necessarily multiple objects, but yeah.
Speaker 4: Well if you have 10 a1, or 10a objects.
Kyle Simpson: Yep.
Speaker 4: And you want to share identify.
Kyle Simpson: Yep. But I'm suggesting that delegation, the way we're talking about, it could be useful even if there was only an a1.
It doesn't have to be multiples, but multiples are certainly useful.
Kyle Simpson: Where we're going when we get to the punch line of all this, when we talk about delegation as a design pattern as opposed to classes, I'll show you why I think that's an important thing to uncover.
What it all boils down to is that these are not copy mechanisms, these are prototype linkage behavior delegation mechanisms.
Kyle Simpson: So what we finally do is we finally come back to this diagram, the diagram I gave you before. We should be well versed in how Lexical Scope works on the left hand side but we now understand how this, plus prototype, describes what happens on the right hand side.
That this key word tells us which building to go into, which address of the building downtown to go into. The prototype mechanism tells us how we're going to find properties if they don't exist on the direct object. When we say a1.identify it first looks on the first floor of a1 and if it doesn't find it there, it goes up one level, traversing up one level of the prototype chain, going up one floor of the elevator.
Goes to the second floor, goes to the third floor, goes to the fourth floor, it keeps going to the top until it gets to the top, which is object prototype. It's the analog to the global scope when we are doing lexical scope. Remember, these two mechanisms, while they operate sort of similarly in that both of them have elevators, the elevators do not cross over in any way shape or form.
They are two orthogonal mechanisms.