Understanding Functions and 'this' In The World of ES2017

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
my name is Brian Hughes I'm a developer evangelist at Microsoft and you can find me everywhere online at net RIA so on Twitter github stuff like that and so today we're gonna talk about kind of a perennial topic in JavaScript we're gonna talk about how this works in the context of JavaScript now we're going to talk about something I haven't seen as much discussion on recently and that's how that this works with a lot of the new features that have come into the language over the last couple years because they actually kind of change how this works a little bit of a first we'll talk about the basics of how this works this may be review for some of you but it's really important that we understand some of the foundational basics of how this works in JavaScript and so for this section everything we're gonna see is actually gonna be es5 code so it will have worked in browsers going back to I was it like 2009 2010 something like that so first up we have a very simple function it doesn't really do much it's just a function kind of sitting there and it's what's called a function declaration so we have this function we call this function and we check to see what this equals we're comparing it to global Global's of something specific to no js' if you're in the browser then this equivalent would be window but same basic thing and we see that we call this basic function and we see that this pointer is equal to global so all right that's kind of our first little data point in this but let's make this a little bit more complicated so now we have an object we create this object here science-u variable come my object and then we have a function called my function so we call my object that might function and we check to see what this pointer is inside of this function and now we see that this pointer is set to my object by the way I suppose I tend to call it this pointer a lot because I come from a C++ background it's not exactly accurate in JavaScript but he go what I mean oh yeah so we see that this in this case equals my object it's no longer global but we kind of expect that to be the case you know this is meant to refer to the calling context of a function which is usually the object that that function is associated with and like this is this code would work in in other object-oriented language at least that supports the syntax so if you're in C++ or Java c-sharp something like that you have an object and you have a method as part of that object and you call that method at this point well this in C++ Java and c-sharp will also equal my object so this is kind of like what we expect this is sort of the default assumption of how this works in JavaScript because this is how it works in every language we start to change things up a little bit in JavaScript and all of a sudden the way JavaScript works deviates pretty strongly from other languages so let's say we do this the object declaration is exactly the same as we have before but instead of calling that method directly where instead gonna assign it to a variable first we're gonna go my function equals my object about my function so it's the exact same function you know we haven't really changed that object at all we didn't like remove that we didn't like delete my function from my object it's still sitting there but now it's on a variable and so we'll call that variable directly and all of a sudden this no longer equals my object is actually changed and it turns out in this case that this is back to equaling global like we had in that very first example and so like this is pretty confusing this tends to trip up a lot of people because it feels like this changes on us it's like something changed this like wow our code was running what turns out it nothing actually changed it has to do with how JavaScript creates that this value where it comes from it's actually a pretty straightforward rule but it's really counterintuitive so the way this works in JavaScript is if it is called in this form object dot function or if you did like object you know square bracket string function either way it's the same but if we have an object reference followed by a function in the code like it sits there in the text of your code then it's going to take whatever this value is sitting in front of it it doesn't matter what it is but it'll take that value and we'll set this equal to that value but if it's not called like this if there's not a dot and not a square bracket right there in front of our function it's going to assign this to global it's just kind of like a default this value is always going to be global or once again if we're in the browser like this is how this works in all of JavaScript it's actually just this rule right here so it's it's one of those things that almost can seem a little overly simple and it's different from how every other language works so it tends to cause a lot of confusion but this is the rule like if you remember this rule everything else comes from that and we see this if we look at a little bit more complex example like this so we have my object again we have my function just like we did before and we're gonna do this console log but then we're gonna have this little set timeout right here this could be any asynchronous method you know it could be a file system reader a database call or anything like that anything where we have a call back and so we're gonna call my object that my function just like we did before and so this console once again this is exactly like we had before this equals my object but then in here we see that it actually gets flipped around so on this line line 3 this equals my object and on on line 5 this no longer equals my object it equals global instead it's like this is something that shows people oh because it's like oh this is one thing here and then it says totally different thing here even though that's the same block of code the reason is going back to that rule on this page or on the slide right here so if we follow these rules okay this first function is called right here so we had that reference and we see that my object is being referenced from my function as being a reference from my object so we're gonna set this in this function call equal to my object but there's no such object reference right here so set timeout I mean this is little indirect but inside if we kind of peeked inside the VM it's essentially as if they were calling that function directly so there it's just a function called there is no dot reference in front of that function when set timeout calls it and so it follows back to that sort of default rule and we'll assign this equal to global so we're just following those same basic rules and but we see how we kind of get this different behavior so we kind of have to be mindful whenever we're writing our code there's a couple other subtleties in JavaScript that you know slightly off-topic perhaps but I think it's really important and this is the difference in what we call function expressions versus function declarations these are the formal names for them they're you know in the spec we can declare functions in two ways and es5 specifically we can go function some main parentheses and then curly braces or we can go VAR my function expression equals function over here and so they look like they do the same thing and most people tend to use them interchangeably and they are very very similar but they're they look different though of course in this one there's no VAR there's no equal sign and they're called this it's kind of a language theory type of thing but they're called this because this cannot sit inside of something else right this is kind of its own statement we can think of it as having a semicolon at the end javascript we don't have to but we can kind of think of it like that this is sort of equivalent to like an if statement you know like you can't go VAR my var equals if right you know that's just not allowed same thing with a function declaration but we had this other rule that looks pretty much identical but actually follows a different part of the spec if you dig into it and that's a function expression and that's where this can be a value and this can exist inside of some kind of assignment with some kind of expression like you could even if you wanted to for some reason you could do function parentheses you know curly braces plus one like you could actually do math on that there's not really reason to you're just gonna get not a number but you know hypothetically you can do that according to the spec so they look a little different but then let's also take a look at there is a bit of a behavior difference so let's take a look at that so if we have a function declaration let's say we define our function here then we call it above we call it a for we've declared the function not after this works completely fine and that happens in JavaScript because of a thing called hoisting which you may have seen before and JavaScript the wave variables work whenever you go VAR you know my variable equals some other thing well the engine actually kind of splits that into two pieces it's gonna take that var name thing and it moves it to the very top of your function or your file under the hood like it's like it exists up here kind of at the very top but then the assignment part still happens below with a normal variable now this also happen with function declarations but since there's no assignment it basically just moves this entire thing up to the very top okay under the hood kind of inside the engine and so this works just fine but it breaks if we use a function expression because what this is gonna do here like I said you know we take that first part the VAR name and we hoist that to the top so I basically have VAR my function semicolon up here and then down here we end up it's like it gets rid of this bar and just has my function equals function it's kind of what's going on under the hood so we think about this from the intents place back perspective we say VAR my function without anything which implies that it's set equal to undefined right here like that variable doesn't actually get this value the function until it gets down here after he tried to call it so we get an exception so this is another thing to be careful that can kind of trip people up this is why I actually recommend always these function declarations never use function expressions unless you have to because this can this is just kind of easy to get tripped up by I even get tripped up whenever I actually do this sometimes now of course things get even a little more complicated with function expressions we actually can have what's called a named function expression and we can actually we can this is totally valid JavaScript we can go VAR my function equals function my other function like notice these names are not the same but we can totally do that this actually works but there's some weird things with where these names are available so this variable is once again is you know hoisted so it's kind of like it exists up here at the top so that's available to the entire file but this name right here is only available inside of this function it's not available outside of it so if we try to call my function down here that works fine because we have the same here but if we were trying to call this name we actually get a reference here it says sorry I don't know what this variable is I haven't seen it but then inside of it we can actually call both and by the way all of this code I actually ran that's why I have a little bit of logic here because I always get an infinite loop so just another little thing to be aware of but like this is the basics of functions in JavaScript and everything is built on top of this now I mentioned earlier that you know we have this this value and it wasn't exactly what we wanted this with an asynchronous call so how do we get that this set to what we want well JavaScript has three things or three methods we can use to actually manually change the dis value of a function those are call apply and bind so with call this allows us to do something like this we create you know this object with our function just like we did before a couple of comparisons in here it's got a couple of arguments and then over here we create just some other random object we just want to have an object hanging around and so we go my object dot my function dot call it's like we're accessing a property on this function that is itself a function and the first very parameter that it takes is actually what we wanted this value to be so we could just manually set it to anything we want and when we set it to my other object and we do this comparison we can actually see that this with this comparison it does indeed equal my object and then all the parameters that come after this are then passed as a normal primer so we can see the second parameter here gets assigned to this first one up here it's kind of like it internally just slices this off after it's applied it that is also apply which is almost identical to call it looks really really similar the only difference is that instead of having each successive parameter being map sort of one to one up here we supply an array in this array gets mapped to those parameters right there like I said it's almost exactly the same just a slightly different syntax but this does in able some really useful tricks we can do specifically if we want to like pass values through if you want to have some function that sort of sits in the middle of another function you know say this is debugging or something we can actually go my function apply this comma arguments we can take that arguments keyword and JavaScript let me just drop it right in here and that way we can kind of like chain along our arguments without having to actually inspect them and see what they are so that's kind of cool and then finally we have bind and so bind works a lot like call except it breaks it into two separate steps so once again we have the same object like we had before my other object and here we say my object my function bind and what this does is it's going to return a new function that has the this value kind of pre applied to it is how we can think of it and so we can assign that to a variable and then we call that like a normal function so this call right here where we call this it looks just like we didn't even do this except we have changed what this pointer is and so the bind is what we typically use if we need to kind of sort of force this point out of something that we're gonna pass is say a callback or something like that it's like that's the basics of how this works in JavaScript starting with es5 and is almost exactly how it worked in even older versions of javascript - there's just a little weird edge cases before but so now we're passed ds5 we've gotten a lot of new features and there's a couple of key additions that kind of have some implications for how this works the first one is object shorthand notation so now we can create an object that looks like this so first of all you notice i've switched to using constant instead of art and another new addition to es 2015 we have my object equals this and so we have my function like before myself having to do my function colon space the word function and then parentheses we get to get rid of that colon in that function I'm just kind of shorten it up cleaning it up a little bit but I like this I think this is more readable myself it's just like let's stuff to kind of have to croc and so then we can call my object op my function and we can see that this is equal to my object just like before then we do this aliasing and we see that this value changes so this looks just it looks exactly and behaves exactly like when we did my function call it our colon the word function in parentheses it's just shorter and indeed this is syntactic sugar over the old way of doing it under the hood in the engine it acts exactly the old way it's going through the exact same code path but there is something else new that we got and that is arrow functions so arrow functions are one of my favorite features in es2015 because it really changes how we kind of write our code and so it looks like this and I wrote this kind of like a function expression um arrow functions only exist in in function expressions there's no like function declaration equivalent a function we can take you know constant my function equals and we have parentheses then we have this equal sign and this little greater than sign right here and then we have our function body right after it so it's shorter it's your characters we don't have to type out you know fu NC tion so it's your characters shorter which is nice and yeah that's kind of the behavior we have here like if we look at this code it's behaving exactly like that first slide we saw so you know we call this function there's no objects or anything like going on and this indeed equals global the thing we go is the more complicated case so we have our object inside of it you can see we have this object or yeah object shorthand notation like we saw a couple slides ago we have a set timer but this time we had the arrow function instead of a function expression so we call my object my function this is set to my objects you know just like before so once again nothing has changed here but then inside this set time we notice that this now equals my object instead of global this is reversed from what we saw before and so before I explain that I have one last a little bit of code that I actually didn't know before I started writing the suck it actually surprised me a little bit but I think is really cool so we create random object up here we have our fat arrow function we're gonna do my function call my object you know first we just call it and we say we see that this is not equal my object we expect that but then we do my function call and we explicitly tell it what this value we want but it ignores it this is still not equal to my object even when we do call and it turns out that call apply and bind have no effect on arrow functions they're basically ignored and that's because under the hood what's kind of going on is if you look at the spec for a normal function there's like a whole section dedicated to how you figure out what this is and there's like all sorts of like conditionals and stuff like that it's like it's a little complicated actually from a specification perspective but if you look at the fat arrow spec it's it just skips all that it doesn't have any of that stuff there there's no logic to actual or no ger them to really determine this it simply takes whatever is outside that function and applies it internally so in this case you know this external this is going to be global because that's just what this isn't a file so it's gonna apply it on the inside too and we have the same thing here it takes whatever this value is right when we created it so it's gonna be kinda like right in this part of the code and just applies that internally so this way we get this sort of like chained this value going to wrap so now we get to skip doing call apply and bind because this kind of does it for us but does it in a stronger way it means that libraries under the hood can't mess with that this value and some libraries do that all right so let's talk react a little bit I know this is the no conference but I have a reason for talking about react because it illustrates another issue that we still kind of have with this and where gets complicated so we have code that looks like this we have a component that extends the sort of react based component class this is how we create all components we have a little click Handler we're gonna call this handler whenever someone clicks on this button right here and specifically because we wire that up right here we give it this little on click property to our button and we say on click equals this dot on click so we added this reference right here however under the hood and react what happens is you know this sort of falls away it just gets a function expression this is actually just like when we did variable a listing earlier so when react calls on click this is all wrong it's actually it's set to global or well window in this case because it's a browser and we can log and we see that this is not my component in this matters because in a react component there are two key properties on the object what is called props in the other called state and we have to work with those just to figure out stuff about the component and how we want to respond to this click event so we have to have the correct this right here but this doesn't work so we have to change that well one way we can do that is to use a fat arrow function arrow functions they were originally called fat arrow functions for reasons so you might Kimmie chop that in occasionally so we have an arrow function here that then calls this dog and this works this actually sets to this pointer we want correctly because this this arrow function right here takes to this value from just outside of it which is set correctly now this is not ideal and react if you've heard about reacting out it's a UI framework for the web and it's known for being really performant and the way it does this is with what's called a virtual Dom it has this in-memory representation of what the entire UI state is and whenever it's time to rerender it actually recreates that entire tree from scratch every single time because it's a memory that's really fast but then it has to synchronize those changes to the actual Dom itself which is slow the naive way would be just copy everything over but it doesn't do that it looks to see what has changed and only copies over the bits that have changed now whenever we come into this render function you know we're creating a new function each time so since we're creating a new function reactions can look at this and be like oh the property changed and it's gonna think it always changed even if it didn't so this is actually a performance gotcha and not a good practicing yet so instead they recommend doing this we have a constructor for a component that gets called whenever this thing is first created and we call a super constructor and then we do this stunt on click equals this stud on clicked bind this so we're back to using bind so that we can set this value to what we want and then whenever react calls does we're back to using this we don't have that thought that arrow function anymore we get the correct this pointer and we also get the performant nature that react really wants so this is how react recommends we do it right now and it works kind of a boilerplate II a little bit verbose so not really ideal and we have to kind of remember to do this and it's can be kind of subtle until you've worked with it for a little while and know that we have to do that so this is where we start looking ahead to the future there's a new proposal out there that's really going to change this and that's called instance properties or well it's kind of built on this so in 20s 2017 sorry this is how we do it right now if you want to have a property on a class something that's not a function we have to assign it in the constructor we'd say this not my property equals 10 right and we do that inside of the constructor and then we get a nice little property we can log it and it sort of works so we would expect but this is kind of a runtime sort of thing you know this is we're writing code to do it we can have it's complex expression as we want but you know this is it makes a little difficult for tooling and various other things like that in addition to being verbose like if we have some tooling that's trying to figure out that helps you with say refactoring a side of your IDE it can be feat difficult to figure out what your properties actually are because they can be interspersed in all kinds of complex coding be hard to figure out so there's a new thing out there called class field declarations that's coming out this is at stage 3 of the TC process of the tc39 process of getting something in so whenever someone wants to add something to the language you know they submit some proposals and it goes to this process it started talking to save 0 which is like basically like I have a rough idea I don't know what it looks like and then it gets refined over time it goes through saves 0 to 1 to 2 to 3 and then to 4 and these just have to do with like how we get something into the official spec and into browsers so this is pretty far along it's not done but it's really close and unlikely to change and so what we can do now is inside our class we can say my property equals 10 you know we don't have to have it inside the constructor anymore it just sits there and we can do a new instance we can log it and it works so this is pretty nice-looking we like this now this has some special implications though especially around functions because now we could actually do this we can create a property and we can give it an arrow function and at this point our course works differently for arrow functions that have so we've been talking about and that is still true here mning a little bit special though that's really nice so when we have an arrow function that's assigned to a class field property then this value this automatically gets pulled from that class instance it does at every single time and it's something that can't be changed by call apply and bind and so with this you know we can see that with this code here we actually alias this function to a variable and call it and we see we still get even with this a listing we still get the this value that we want so that's like really cool and we finally have methods for classes and then this way that works in JavaScript the way it works in Java C sharp and C++ I think this is nice because it's really consistent we don't have to think through like Oh what are the rules again exactly which rule is taking precedence over which one it just works and it always works the same way and so if we act specifically we can rewrite that class to now look like this so we have in our on click method again but we do it as a classical property with an arrow function and we're back to the code that we had on that very first react slide except instead of being object shorthand notation it's an arrow function and so we have onclick equals this sivan click but because this is already bound we don't have to in the constructor do that you know this doesn't click equals this at all clicked up buying this like we get to leave it out entirely and we get something that I honestly think it's more readable too there's just like this left less boilerplate and corrupt and we can focus more on the exact problem and so it's really cool to see like how the language has been evolving and JavaScript is very much an evolving language must being in they do a test every day at noon if you're wondering what that is so yeah javascript is an evolving language and it's like we're getting all these little niceties that for the most part kind of papering over the rough edges of the language and they make it a little easier to use without getting cut by some weird like errata you know edge case in the spec and we're gonna keep getting even better after that so it's really exciting to see where we're gonna get but that oh I thank you all for listening [Applause]
Info
Channel: Coding Tech
Views: 29,548
Rating: 4.9026027 out of 5
Keywords: javascript, es7, es.next, functions in javascript, 'this' keyword
Id: AOSYY1_np_4
Channel Id: undefined
Length: 25min 52sec (1552 seconds)
Published: Sat Oct 13 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.