Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

JavaScript's "this" vs Python's "self"

00:00 In the last three lessons, I’ve been covering various quirks and gotchas of the JavaScript language. This is the last lesson devoted to quirks, and the whole lesson is devoted to a single thing: the calling context of functions and the this keyword.

00:15 In previous lessons, I’ve used the this keyword within the context of functions inside of objects. The concept is similar to the use of self inside of Python.

00:25 If you’ve ever had the thought, “Why do I have to explicitly define self? Why can’t it be done magically for me?” Well, the rest of this lesson will make you swear off magic.

00:35 The problem with this is how it magically gets set. Unlike Python’s self that is explicitly passed into an object method, the value of this is based on how the function it is contained within is called.

00:50 It gets complicated when you start getting functions inside of functions, and JavaScript frequently does this. To further complicate things, the arrow function behaves differently. It’s actually much cleaner but mixing and matching it in code can cause surprising results.

01:07 On this slide, you’ll see the this keyword used in three different situations. First off, I’ve assigned it to the global variable x.

01:16 This highlights how this is different than self in Python. self is always within the context of an object. In JavaScript, the context is based on the calling function. If there is no calling function, then this means the global context. Inside of a browser, this is the window context, but similar idea.

01:39 In a global-level function such as print(), the context of this is the global context.

01:46 The third segment here is an object called a john. This is similar to how you’ve seen me use this before. Within this context, the value of this is the containing object.

01:58 It may be helpful to think about these three different contexts by considering what is calling the function. To call the .cleese() method, you write john.cleese(). Calling the print() function is actually a short form for calling global.print(), or window.print() in a browser.

02:16 The context of this is what’s on the left of the dot (.) in the calling statement. So when you call john.cleese(), this is the john object.

02:26 When you call print(), that really means you’re calling global.print(), and this is what’s to the left of the dot, which is the global context.

02:35 This is important to understand before I make it more complicated. In Python, self is the object. In JavaScript, the context is based on how the function was called.

02:46 This has consequences with nested and anonymous functions.

02:52 Let’s look at context within a more complicated object. The fruit object has an attribute called .items that is an array of 'apple', 'banana', and 'pear'. There’s also a method called .show().

03:06 Within that method, the context of this is the fruit object itself. So far, this is the same as the john object on the previous slide, but here’s where it gets a little messier. Within the .show() method, I want to iterate through the different fruit in the .items array.

03:23 A convenient way to do this is using the .forEach() method on the array. The .forEach() method takes an anonymous function, which it calls for each item as it is visited in the array.

03:36 Within the context of this anonymous function, the this keyword is no longer bound to the fruit object, but to the global context.

03:45 Think back to the “How is it called?” concept I mentioned previously. What is to the left of the calling dot of an anonymous function? It can’t be fruit because you’re not inside of the calling function on fruit.

03:58 Anonymous functions end up in the global context. The resulting consequence is your this keyword no longer points to the fruit object. In fact, without taking additional steps, you have no way of getting at the fruit context here at all.

04:16 In a past lesson, I introduced you to the arrow function. I explained it as a shortcut for defining functions. Well, it has another important aspect. It treats the this keyword differently.

04:27 In fact, it doesn’t use it. Within the context of an arrow function, the this hasn’t changed. It can be accessed, though. Consider this .display() method. The first this is the fruit object, as this is inside the member function .display(). The difference between the .show() method and the .display() method is how I’m calling the .forEach().

04:50 This time I’m using an arrow function inside of the .forEach(). As the arrow function doesn’t rebind the this value, it will still be whatever value it was before, which is the context of this inside of the .display() function, which is the fruit object.

05:07 This is a really good reason for using arrow functions inside of object methods. It makes the this behave an awful lot more like self in Python.

05:17 One word of caution, though. The lack of context in an arrow function can be a problem as well as a solution. Consider this last piece of code. By using the shortcut to define the method .spear(), you aren’t going to get the value of this.

05:32 If you stopped the code at this point and examined the value of this, you’d find it was an empty object. When I first came across the arrow function I wasn’t aware of this difference.

05:43 In fact, at the time, I thought the this keyword was like self in Python. A very frustrating afternoon followed by a chat with a friend of mine cleared a bunch of this up. If, like me, your mental model of this is based on other languages, you can be for some surprises, and not the happy birthday kind but more like the parking ticket kind.

06:03 There are other solutions to the context problem than just the arrow function. A common workaround is to store the value of this in another variable—a common name for it is that—within the scope of nested anonymous functions.

06:16 The value of that will still be available in the original object. Some functions, like .forEach() on arrays, take a parameter that allows you to set the context. When using .forEach(), you can pass in the current context of this and .forEach() will set that context inside of the anonymous function.

06:35 This is handy, but it isn’t available on all functions. But if you’re using .forEach(), you can take advantage of it.

06:42 JavaScript provides three built-in functions that allow you to explicitly set the context for a calling function. The first is .apply(). Use the name of the function you were going to call and then call .apply() on that function name instead.

06:57 You can pass in context and arguments for the named function. The .call() built-in is exactly like the .apply() built-in, but instead of taking an array of arguments it uses multi-argument syntax.

07:11 And finally, the .bind() built-in takes the named function, binds the given context, and returns a new function. You can then call the new function from anywhere, and this will be set appropriately wherever you are. .bind() is pretty powerful, but it can make for some hard-to-trace code.

07:28 If the .bind() happens in a different file or hundreds of lines above, it is easy to forget that the context has been changed and not correctly understand the value of this when you’re reading the code.

07:40 Everything on this slide feels kind of hackish now that arrow functions exist, and I don’t mean hackish in the “I’m proud of the workaround I’ve found, isn’t this cool?” More of the “I’m ashamed it has come to this,” kind of fashion.

07:54 Well, now. It’s been a long journey. In the last lesson, I’ll wrap up and summarize everything that’s been covered.

Become a Member to join the conversation.