JavaScript’s “this” Explained By Starting A High School Band

If you have ever been in a band, had a friend that started a band or seen a corny 80s movie about starting a band, then you can understand the concept of “this” in JavaScript.

When you are reading over some JavaScript, and you come across the this keyword, the steps you need to take in order to figure out its value seem obvious.

You might be thinking, “I just need to find the function that contains this, and then I will know what it is referring to!”

let band= {
  name: "myBand",
  playGig:function() {
      console.log("Please welcome to the stage" + this.name);
  }
}

In the example above, for example, this.name refers to the name “myBand”. This seems easy!

But, as you learn more JavaScript concepts, like closures and callbacks, you will quickly find that this does not behave like you would expect.

So, I wanted to create a visual explanation of how this works in JavaScript. Here’s the scenario: You are back in high school, and starting a band with your friends (or maybe you are currently in high school?)

  • Your band has 4 members
  • You play three types of gigs- you play at bars, school competitions and public events in town.
  • Your team can play all types of music, so you try to choose the right songs to match the audience. You don’t want curse words or sexual references at the family-friendly events, for example.

As you will soon see, the biggest concept you need to understand with this is execution context. That is what determines the value of this.

Before you use this tutorial, you need to understand objects and variables. Check out my tutorials on each of those subjects if you need to review.

If you are interested in a more technical version of this tutorial, check out the guide from JavaScriptIsSexy.

globalcontext

The Global Execution Context

Let’s say that your band needs to do a family-friendly gig at the local park or as part of a local fair. You need to choose the right type of music that will keep parents happy and also not offend anyone.

Let’s say that you choose to play some Billy Joel (a famous American artist), and even though this is not your favorite, you know that it’s what you need to do to get paid.

Here is what that looks like in code.

//The songs you will play
let artist= "Billy Joel";

function playGig(){

  //instruments that your band will use
  let instruments= ["piano", "microphone", "acousticGuitar", "harmonica"];

  console.log("We are going to be playing music from " + this.artist + "tonight!");
}

playGig();

 

billyjoelinstruments.png

In the example above, we have an artist variable that indicates what type of music we will be playing. And we have an array full of instruments that will be used to play that music within the playGig function.

In the last line, we call the playGig function. So what is this.artist, in this case?

Well, first we must determine the execution context for this function. The execution context is determined by the object that the function is called upon.

In this case, there is no object listed, so that means that the function is called on the window object. It could also be called like this:

window.playGig()

"We are going to be playing music from Billy Joel tonight!"

This is the global execution context. The function is called at the level of the global object, window. And, the variable artist is available as a property of the window object (see this note on the JavaScript specification).

So, in line 1 of the snippet above, we are also saying:

//old version- let artist = "Billy Joel";
this.artist="Billy Joel";

 

windowexecution

Your band is executing the gig on the global context by playing music that appeals to everyone (unless there are any Billy Joel haters out there).

 

barpic

Object-Level Execution Context

Let’s say that your band got a gig at a local bar. This is great! Now, you don’t need to play music that satisfies everyone in town. You only need to play music that people can dance to.

barvglobal

Let’s say that you choose Coldplay, since most of their recent songs are pop music. You need a piano, microphone, drumset and guitar for this gig.

Let’s create a bar object with the same pattern as we created for the public park gig.

//The songs you will play in the public park/fair
let artist= "Billy Joel";

function playGig(){
  //instruments that your band will use
  let instruments= ["piano", "microphone", "acousticGuitar", "harmonica"];

  console.log("We are going to be playing music from " + this.artist + "tonight!");
}

//NEW PART

let bar = {
  artist:"coldplay",
  playGig: function(){
    //instruments that your band will use
    let instruments= ["piano", "microphone", "guitar", "drumset"];

    console.log("We are going to be playing music from " + this.artist + "tonight!");
  }
}

Here is the diagram for the code above:

barobjdiagram

So, let’s say that we want to write the code to get the gig at the bar started. We need to watch our execution context, which is the bar object in this case. Here is what that would look like:


bar.playGig();

//"We are going to be playing music from coldplay tonight!"

And, we can still execute the playGig function on a global level, and we will get a different output. This is great news, since we do not want to be playing Billy Joel or Coldplay at the wrong venue…

playGig();
//"We are going to be playing music from Billy Joel tonight!"

So far, this has been the easy stuff. Whenever we have been calling a function, the object that provides the execution context has been pretty straightforward. But that is about to change as we get more complex.

schooljam

Changing Execution Context using jQuery

It’s the big event that has been covered in every single movie from the 1980s: The Battle of The Bands! Yes, every band in your high school is going to get into a competition to see who is the best.

You are going to play some songs from AC/DC, pretty much the coolest band on the planet. But in order to do that, you need a different instrument mix than before:

  • A microphone
  • An electric guitar
  • A bass guitar
  • A drumset

Let’s call this the battle object. Here is what it looks like in code.

let battle = {
  artist:"acdc",
  playGig: function(){
    //instruments that your band will use
    let instruments= ["microphone", "electricguitar", "bass", "drumset"];

    console.log("We are going to be playing music from " + this.artist + "tonight!");
  }
}

Since this is an annual event, we are going to use a click event from jQuery to start your show. Here is what that looks like:


$('#annualBattle').click(battle.playGig);

But if you actually ran this code… it would not work. Your band would forget the words and the notes, then slowly walk off the stage.

To figure out why, let’s return to execution context. We are referencing a DOM element called #annualBattle, so let’s see where that fits within the window object.

DomObjectContext

Since #annualBattle is an element in the DOM, it is part of the document object within the window object. It doesn’t have any property called artist. So if you ran the code, you would get:

$('#annualBattle').click(battle.playGig);
//"We are going to be playing music from undefined tonight!"

In this case, the execution context is an element from the DOM. That is what kicked off the click() method, which used the playGig function as a callback. So, this will end up with an undefined value.

In our analogy, this means that your band showed up to the competition with all your instruments, got in position to play, and then stared at the crowd like they were going to tell you what to do.  It means you have forgotten the context of why you were there in the first place.

To solve this, we need to use the bind() method to make sure that the playGig method still references the battle object, even when we call it from the context of a different object! It looks like this:

$('#annualBattle').click(battle.playGig.bind(battle));
//"We are going to be playing music from acdc tonight!"

Now, we get the correct output, even though the context was a DOM element.

Pulling A Function Out of Context

Let’s say that we wanted to write the code that will allow us to practice for the Battle of the Bands event. We will create a separate variable called practice, and assign the playGig method from the battle object.

let artist= "Billy Joel";

function playGig(){

  //instruments that your band will use
  let instruments= ["piano", "microphone", "acousticGuitar", "harmonica"];

  console.log("We are going to be playing music from " + this.artist + "tonight!");
}

let battle = {
  artist:"acdc",
  playGig: function(){
    //instruments that your band will use
    let instruments= ["microphone", "electricguitar", "bass", "drumset"];

    console.log("We are going to be playing music from " + this.artist + "tonight!");
  }
}

let practice = battle.playGig;

//run a practice
practice();

So you are probably wondering… what is the execution context of the last line?

Well, this will run into a similar problem as the previous example. When we create the practice variable, we are now storing an instance of the playGig method in the global context! It is no longer in the context of the battle object.

practiceGlobalDiagram

If we ran the code above, we would get:


practice();

//"We are going to be playing music from Billy Joel tonight!"

Not what we want. We are trying to practice AC/DC, and instead practicing Billy Joel. Yikes.

Instead, we need to use the bind() method just like above. This will allow us to bind the context of the battle object.

let practice = battle.playGig.bind(battle);
practice();

//"We are going to be playing music from AC/DC tonight!"

How Anonymous Functions Affect Context

Let’s say that your gig is coming to a close, and you want to give a shoutout to everyone in your band so that the crowd can give each person a round of applause.

In order to do this, we are going to use the forEach() method to iterate through each element in the value of the instruments property. (You will see why we changed it from a variable to a property in a moment). It will look like this:

let battle = {
  artist:"acdc",
//instruments that your band will use
instruments: ["microphone", "electricguitar", "bass", "drumset"],
  shoutout: function(){

    this.instruments.forEach(function(instrument){
       console.log("Give a shoutout to my friend for covering the "
+ instrument + " from " + this.artist + "!");
    }
  }
}

battle.shoutout();

But yet again, if we ran this code, it would not work.

It all centers around the line where we declare an anonymous function to use on each element in instruments. When this function is executed, the first this will retain the correct context: the battle object.

But, when we arrive at this.artist in the console.log statement, we will get… “Billy Joel”. This is because of the anonymous function that is used as a callback in the forEach() method. It resets the scope to the global scope.

anoncontext2

n this case, that means we would claim at the end to be playing Billy Joel… d’oh!

But here’s what we can do. We can create a new variable called that to store this in the correct context. Then, when we reference the artist that we played in this specific gig, we can reference the stored context, rather than being forced to return to global context.

let battle = {
artist:"acdc",
//instruments that your band will use
instruments: ["microphone", "electricguitar", "bass", "drumset"],

shoutout: function(){

//store context of this
let that = this;

this.instruments.forEach(function(instrument){
console.log("Give a shoutout to my friend for covering the "
+ instrument + " from " + that.artist + "!");
}
}
}

battle.shoutout();

 

Get The Latest Tutorials

Did you enjoy this tutorial? Check out the rest on the main CodeAnalogies site, or sign up for the newsletter here:

 

 

 

6 thoughts on “JavaScript’s “this” Explained By Starting A High School Band

  1. Great tutorial, I think that in order to make it even better you can also include arrow functions. Thanks again, great article.

  2. Loving your website and explanations…
    On that one though, I have two objections.
    First is that you mention jQuery : you shouldn’t. I started learning JavaScript 8 months ago and as far as I understood, JS is evolving in a way that jQuery won’t be needed, ASAP. It means that I didn’t learn it, and a lot of my fellows new in the web aren’t learning it either as it’s becoming more and more useless. So, as your explanations are mostly for beginners, you should adapt your content, shouldn’t you ? What do you think ?
    Second is that you didn’t mention arrow functions. You kept all along with ES5 whereas you might mention ES6 also as arrow functions are changing the context. What do you think about that either ?

    1. Hey Arnaud, you make a good point. It’s really hard to consider both past technologies and what seem to be the most popular tech of the future, like arrow functions. As for jQuery, keep in mind how many active jobs will use jQuery knowledge: https://medium.com/javascript-scene/top-javascript-libraries-tech-to-learn-in-2018-c38028e028e6

      It’s the second most popular behind React!

      Arrow functions- think about how much legacy code will NOT include them. That being said, I do need to create a tutorial on them at some point. Perhaps after I write a tutorial on my blog about it, I will start using them in code snippets.

      Appreciate the feedback!

  3. Hey Kevin, great stuff! I was just left wondering why an anonymous function changes the scope to the global context. I believe it would set a stronger foundation and understanding if you would dive deeper into the reasons behind. In this case I would just add something like “In the forEach method, the value of “this” would be set to undefined unless an argument is specified as the object to bind “this” after the function declaration, determined by the curly braces i.e. { }, here”

    1. Hey Jose, thanks for the feedback. It’s a little tough to explain because that is just the way JavaScript is written. An anonymous function is technically called on the window object, so maybe I need to do a better job of explaining how the window object works compared to objects that you see in my code?

Leave a Reply