Last Updated: October 11, 2021
·
4.509K
· jjperezaguinaga

What's this? What's this? There's something very wrong

Picture

this is probably one of the hardest concepts to grasp in Javascript. Not because it's complicated, but because it's one of things of the Javascript language that developers need to memorize in order to understand how it works in specific scenarios. I'll try to explain what is this all about and yes, there are going to be tons of puns.

What's this? What's this?

First, this is a pointer, a reference to an object. It's created whenever a function it's created and lives inside its scope in the same way the arguments variable. One major difference with the arguments variable is that you can't modify it's value.

function() { arguments = [] } // no error
function() { this = {} } // throws ReferenceError: Invalid left-hand side in assignment

The object this points to refers to the object of which that function is a property/method. In Web Development, most of the time we are calling functions through the Global/Head, the window object inside our Browser, where all our Javascript object is being stored. In your favorite console, if you type this, you get the aforementioned Global/Head object.

`this` // returns Window

So in which scenarios does the this object changes its value from the Global/Head object? Here are them:

  • Whenever a function is called, apply or call are used.
  • Whenever a nested function is called
  • Whenever a function is called with the new keyword

Whenever a function is called, apply or call are used.

Let's start with the following object

var pumpkin = {
  name: "Jack",
  status: "King",
  whoGrewSoTired: function() { return "I, " + pumpkin.name + "! The pumpkin " + pumpkin.status}
}

If you create this in your console, you will see that when you call pumpkin.whoGrewSoTired() will actually display a proper message (or lament if you will). This is because the variables pumpkin.name and pumpkin.status are actually browsable from the Head/Global object window. This would be the same as writing

var pumpkin = {
  name: "Jack",
  status: "King",
  whoGrewSoTired: function() { return "I, " + window.pumpkin.name + "! The pumpkin " + window.pumpkin.status}
}

Now, you most likely had never seen this before (if you have, please send me the email of the developer, I will introduce him/her to a Oogie Boogie friend I have). The reason is because most developers in their good senses are aware of Object Oriented Programming (OOP) and what they want is to call the actual properties of the object. And this, my friends, is what this is for.

var pumpkin = {
  name: "Jack",
  status: "King",
  whoGrewSoTired: function() { return "I, " + this.name + "! The pumpkin " + this.status}
}

So how does this changes when we call a function? In the previous code we had no problem in seeing that we are retrieving the name and status of the object pumpkin. Now see the following code:

var season = "Christmas"
var halloweenTown = { season: "Halloween" }
var getHoliday = function() { return this.season }
halloweenTown.getHoliday = getHoliday;
halloweenTown.getHoliday() // returns "Halloween"
getHoliday() // returns "Christmas"

Why are we retrieving the value "Halloween" for town? Well, because the value of this during the execution of that function is the object town, while on the latter it's the object window. Since we are storing all our variables in the Head/Global object, this would be the same as before:

window.halloweenTown.getHoliday()
window.getHoliday()

Now, do you remember those Object methods call and apply? I bet they make more sense now with this example!

getHoliday.call(halloweenTown, null)
getHoliday.call(window, null)

Basically, we are telling to the function getHoliday which value this should have. Let's move on to the next case!

Whenever a nested function is called

Now, there's a special case when this may get a little confused: nested functions. See the following code.

var town = { 
  name: "Christmas Town", 
  king: "Santa", 
  whosTheKing: function() { 
    var town = this.name; 
    var getKing = function() { 
      return this.king 
    }(); 
        return getKing + " is the king of "+ town 
    }
}

Run town.whosTheKing() and wow, you get 'undefined is the king of Christmas Town'. What's this? What happened to Santa? Was he kidnapped? Most likely, but the true problem is that the second this was no longer referring to thte town object! You might say "Well, that's obvious, because this is being called by the whosTheKing function, and not town unlike the first one!". So, would you be happy if I do this?

var town = { 
  name: "Christmas Town", 
  king: "Santa", 
  whosTheKing: function() { 
    var town = this.name; 
    var king = this.king; 
    var getKing = function() { 
      return this.king 
    }(); 
    return getKing + " is the king of "+ town 
  }
}

Run it. "undefined is the king of Christmas Town". If you are shocked by this, keep reading!

(I actually did something in purpose in order to keep this error. To find it, you just need to read this answer in StackOverflow that explains the difference between using var and not using it)

Ok, so what's going on? Here's the answer: In ECMAScript 262 Ed.3, nested functions "lose" the reference to this value. Whenever they "lose" it, they refer to the Head/Global object. So the second function is actually looking outputting the window.king variable. I can prove it here:

window.king = "Jack"
town.whosTheKing(); // "Jack is the king of Christmas Town"

The good news is that in ECMAScript 262 Ed. 5 this is getting solved. In the meantime, a work around that you have probably seen in some code goes like this.

var town = { 
  name: "Christmas Town", 
  king: "Santa", 
  whosTheKing: function() { 
    var town = this.name; 
    that = this;
    var getKing = function() { 
      return that.king 
    }(); 
    return getKing + " is the king of "+ town 
  }
}
town.whosTheKing(); // "Santa is the king of Christmas Town"

In order to not lose the reference, we use a helper variable that stores the correct pointer to our variable. Another work around is to use a Closure that stores the variable with the proper scope. This, however, can get confusing really fast.

var town = { 
  name: "Christmas Town", 
  king: "Santa", 
  whosTheKing: function() { 
    var town = this.name; 
    king = this.king;
    var getKing = function() { 
      return this.king 
    }(); 
    return getKing + " is the king of "+ town 
  }
}
town.whosTheKing(); // "Santa is the king of Christmas Town"

(If you don't understand this, I suggest you to read the note I put previously about the StackOverflow answer)

Whenever a function is called with the new keyword

Ok, so we are almost through all the behaviours of this; the missing one is when the new word comes to town (got it? to town! Ah, fine, I'll stop). Let me ask you, what do you think the value of boogieMan.phrase will be?

var Monster = function(phrase) {
  this.phrase = phrase;
}

var boogieMan = Monster("YOU'RE JOKIN', YOU'RE JOKIN', I CAN'T BELEVE MA EYEZ!");
boogieMan.phrase // returns ??

It will return an error. Why? Because despite our bulletproof OOP design, we are just calling a function that returns undefined (all functions return a value, even if it's undefined). Without the new keyword, you just set up window.phrase instead of an instance of the "class" Monster with a property phrase.

When a function is invoked with the new keyword, the value of this refers to the object itself, which is proper OOP design. From our previous code:

var Monster = function(phrase) {
  this.phrase = phrase;
}

var boogieMan = new Monster("YOU'RE JOKIN', YOU'RE JOKIN', I CAN'T BELEVE MA EYEZ!");
boogieMan.phrase // returns "YOU'RE JOKIN', YOU'RE JOKIN', I CAN'T BELEVE MA EYEZ!"

What if the object doesn't have the property we are looking for? Javascript will start looking up in the prototype chain for the right value, and thus the value of this will be updated accordingly.

var Monster = function(phrase) {
  if(phrase) this.phrase = phrase;
}

Object.prototype.phrase = "Eureka! This year, Christmas will be OURS";
var pumpkinKing = new Monster(); // We don't set the the property 'phrase'
pumpkinKing.phrase // returns "Eureka! This year, Christmas will be OURS"

In the previous code this first looked up in the Monster object; when it failed to find the property phrase, it started the prototype lookup process, where it found the phrase property as part of the Object prototype, the truly king of objects!

Hopefully this helped devs out there to understand better the this word. No Christmas or Halloween towns were damaged in the process of making this article.

9 Responses
Add your response

Good writing. Enjoyed reading it. Keep it up.

over 1 year ago ·

@mutahhir Glad to know you enjoyed it, was worried I made it too tedious. Thanks!

over 1 year ago ·

function() { arguments = [] } // no error

maybe it need a function name

over 1 year ago ·

Thanks for sharing this dude

over 1 year ago ·

Awesome overview! I wonder, when you introduce the that = this pattern, doesn't that become a global variable because you aren't defining a local variable with var?

over 1 year ago ·

Just personal preference, but I prefer _this to that.

over 1 year ago ·

@eallam That's correct! That's pretty much the reason why this.king works if we use king = this.king instead of var king = this.king in the last example of that section. It's stored in the window object!

over 1 year ago ·

@stevefrost I actually prefer self; I am a little kid who was spoiled with python at some point of my life :)

over 1 year ago ·

@jjperezaguinaga while @eallam was correct about creating the global, you don't need a global. You could have used var that = this; and the next function would have maintained a reference through closure.

over 1 year ago ·