Transcript from the "Explicit Binding" Lesson
>> Kyle Simpson: All right, let's learn the third of our rules. This is the second in order of precedence. The third of our rules we'll call the explicit binding rule. Now, the explicit binding rule says if you use .call or .apply at the call site, both of those utilities have, they take as their first parameter a this binding.
[00:00:24] It's called a this arc. So when I say foo without anything, it's a plain, normal default binding rule, we know that. When I say foo.call and I pass in obj, then it says use obj as my this. So we're explicitly stating which this binding we want to occur, and the same is true with apply.
[00:00:44] Dot call and apply do have different behavior with respect to arguments. But with respect to what we're talking about about the this binding, they're one in the same, they have the same behavior, okay? So our explicit binding rules says at the call site, if there's a call or reply that's explicitly specifying a this, use that object for the this.
>> Kyle Simpson: Is everybody following that so far? So foo.call(obj); is gonna make sure to do it, same thing with foo.apply, if we said obj, it uses the obj as this keyword. Let's take another little detour. I need to explain one of the most frustrating parts of all of the this binding mechanism, is this idea that many people complain about.
[00:01:28] You may have experienced this yourself in your own code. Many people complain about the idea that your this bindings kind of get lost, or they accidentally fall through to the global object. It has this annoying property that these functions are very flexible, but that this binding could mean a lot of different things.
[00:01:46] But sometimes we write code in such a way where we want for this keyword to be very predictable. For example, you create yourself a little controller class, a little object with a control that represents different methods for your controller actions. And each one of those methods says this dot to reference various states on your controller object.
[00:02:05] Any one of those methods that you're talking about, you have a very specific object in mind when you reference this keyword and it's your controller object. So any way that method gets executed, you expect for that method to be executed with a this keyword pointing at the controller object.
[00:02:37] So you pass in a reference to the controller method, you say controller.methodname is your callback name and when it gets called the this binding is wrong. It's fallen back to the global. And you pull your hair out and you're frustrated. Or until such a time as you attach that to an event handler like a click handler on a button.
[00:02:54] Whenever I click a button I want for this controller method to be executed. And you get frustrated because now the this binding, somebody made it be the button itself rather than my controller object. In all cases, the this keyword ends up being what we don't want it to be.
[00:03:07] We wanted it to predictably be our controller object. So, there's this frustration that we get into where the, this keyword, it simultaneously gives us all this flexibility but at the same time. I's too flexible in many cases and it frustrates us. Okay? So one solution to that problem is wouldn't it be nice for us to construct a mechanism in such a way that, our methods could be passed around in the way that we are accustomed to passing around, but the this keyword would always be predictable?
[00:03:35] And that mechanism is what we call hard binding. Is there a way to take a function and hardbind the this keyword so that it's a predictable object? So let's take a look at a way we might derive a hardbinding. I will essentially derive the mechanism that's now built into language.
[00:03:52] Okay? So let's take a look at hard binding. How would we do that? Well, so I've got a foo function here, lines one through three. Makes the same sort of reference that we're seeing before. But look at line eight. Line eight's different. I make another reference to the same function, this one I call a ridge, so it's a reference to the original function.
[00:04:09] And then I overwrite the foo. I don't have to overwrite it, but here I overwrite the foo with a whole new function. This case the function expression, now what is that new function that I'm creating coded to do. If you look on line nine, whenever that foo function will be called from now on, he will always call the original function because we still have a reference to it, but he will force the this reference to always be obj.
[00:04:33] No matter how foo gets called, orig will always be called with obj as the this binding.
>> Kyle Simpson: So down here, when we say foo, it's gonna be bar the way we would expect it to be, even if we tried to overwrite it, we say foo.call on line 12, and we say I want you to use obj2 It still would print out bar, and here's why.
[00:04:57] Because inside of this function, right here in this little gap on line 9, right here in this gap, there is a this binding that'll point to obj2. But you notice the rest of the function completely ignores whatever the this binding of the wrapper is. It forces it to be obj.
[00:05:15] That's what we call hard binding. So now we have a reference to foo that if we passed it in as a call back to an ajax call, or if we attached it as a click handler, or any number of other ways that we do a synchronicity. We pass function references around no matter where we pass it, no matter where it gets called.
[00:05:32] It's always going to have a this reference that predictively references obj, cuz it's hard-bound to obj. Does that make sense? So you can see why that's a useful mechanism in some cases. Now, it's reduced our flexibility of our function call, which is kind of a trade-off, but the benefit is now we don't have to worry about people calling it incorrectly.
[00:05:52] So I was gonna predictably reference the object I expected to reference. This is still a little bit clunky though, because we've got this global variable of essentially, there's a ridge that's kinda hanging out, that's kinda clunky. Can we make ourselves a utility to do this, and the answer is yes, we can make a little bind utility that would do the same thing for us.
[00:06:11] So the first step that we might do to create a bind utility, we'll make ourselves a function called bind, he accepts two parameters. He accepts an fn parameter, the function, and he accepts an o parameter being the object that you want to bind for this. Now what does our utility do?
[00:06:27] Our utility creates a whole new function that is hard coded to call FN with O this is this as you see on line three. Same thing as the previous code, but now we've created it as a reusable utility and it doesn't have one of those variables hanging out in the global space to potentially have an overwrite and read reference or something.
[00:06:49] Okay? We use it in th exact way that you would expect. Down here on line 13, we say foo is equal to bind foo to obj. And now when we call foo all by himself on line 15, it still references obj as borrowed. It's not falling back to the global.
[00:07:07] Because this is not the call sign anymore. That's an important nuance. This looks like the call sign. Where is the call sign? CallSite's actually right here. Everybody see that? So subtle nuance but super-important detail. So when you're finding the CallSite, if there's a hard bound wrapper around it, that's that the CallSite anymore.
[00:07:27] You gotta inside the wrapper to find the CallSite. Okay. Well we call it with what would otherwise look like the default binding. In fact, it uses the explicit binding and uses bar and even if we try to override it, it would still use the obj.
>> Kyle Simpson: Questions so far?
>> Speaker 2: The online folks are wondering which Stack Overflow question you were referring to? Do you have a link to it we can get later?
>> Kyle Simpson: I don't have a link to it. This is two years ago when I found it and made this first slide, so I don't.
>> Speaker 2: Don't worry about it.
>> Kyle Simpson: Don't remember it. I just remember tearing my hair out when I saw it. But I don't remember the link to it.
>> Kyle Simpson: Okay, so, the hard binding is nice, but it's still a little bit clunky that we've had to create a global utility called bind.
[00:08:19] Is there a way we could kind of stick that utility somewhere and make it a little bit less global, a little more useful? Maybe potentially useful to all functions. And it turns out that we can put that utility directly on the function prototype. Temporarily for the purposes of this slide only, I'm going to call my utility Bine two, and I'll explain in a moment why I call it Bine two instead of just Bine.
[00:08:43] Temporarily what I'm going to do is check and make sure there isn't already a Bine two. And if there isn't already one I'm going to create one. And what that guy does is he It does this peculiar thing, which I'll explain in a minute. But this thing should look familiar.
[00:08:55] It returns back a new function that's a wrapper function. Now what does that guy do? He's not using a dot call anymore on line six, he's using dot apply so that we can pass along any arguments. And he also has a return in there on line six, so can return any return values back.
[00:09:10] So now, in the previous slide our little bind utility was kinda limited, cuz he didn't have any return values or arguments. That kinda sucks. But this one is kinda the pass through pattern, you'll pass through any arguments and pass back any return values. So just transparently forces this to be o, like we asked for.
[00:09:26] Now what about the trick on line four? We're saying var fn = this. Why would that be the case? Well, let's go and look at the call site. Down here, what we're talking about is that bind2 as a utility, it's a function, so all functions are gonna have a this binding, right?
[00:09:42] Let's look at where bind2 is being called. When bind2 the function is called, what does his call site look like? Which one of our rules applies right here?
>> Speaker 2: It's the implicit binding rule because we have a context object here. And so inside of our utility function, what is this this key we're gonna point to?
>> Kyle Simpson: Foo which is the function we want it to point to. So it's a little syntactic trick. We don't have to pass in the function manually. We can just get it by reference to the dysfunction. So we have our function. We have our o and we can force them to be married together.
[00:11:04] And in fact, not only is it built in, we can go the MDN page, the Mozilla Developer Network page, and I can scroll down. They actually provide a polyfill for the function. What this polyfill is designed to do, is it's designed to allow you to use Function.prototype.bind in pre-ES5 browsers that's what polyfills are for.
[00:11:29] So it has the little if statement to check to make sure that your not overwriting the one that is already there. So you make sure that you're in an older browser. And it defines one and you can ignore some of the details here. You can, what you take away from this code is that it's a lot more complex than what I showed you in that slide of Bind 2.
[00:11:46] So the reason I called mine Bind two, is six months from now I don't want you to go back to the slide deck, and think that the one that I provided is a good polyfill because mine is very limited. If you're going to use a polyfill for the Bind utility, and I recommend that you do Come to MDN and use the one they provide.
[00:12:03] Not only is theirs better, it's more adherent to the spec. It's also more powerful. It has other capabilities that the one I provided you in the slides doesn't have. So this is the spec compliant version of the buying utility represented in pre-ES5 terminology. And some of it's a little confusing.
[00:12:22] A few of the tricks are beyond the scope of what we'll discuss here, but you should notice that it's basically doing the same thing. It's got a function here that it forcibly calls apply with this reference to it. Mechanically, it's doing basically doing the same thing that we've already derived.