The Preface
The this keyword has always been confusing to me when I first started to write Javascript. When it comes to Javascript, this in different scope can mean something else. So lets try to understand it once and for all and hopefully you can walk away feeling completely satisfied that you have mastered this Javascript.
Lets try to understand what this is in plain English. “Hien is writing this blog post, she better know what she is talking about.” We use the pronoun, she to refer to Hien. We can rewrite the sentence as “Hien is writing this blog post. Hien better know what she is talking about.” Technically, the rewritten sentence will work too but it is longer and just writing Hien over and over again doesn’t make much sense. So here comes the pronoun, she or similarly in Javascript, this. We use this as a shortcut to the subject in context or referring object that is executing the code.
var car = {
make: "Honda",
model: "Accord",
getCarType: function(){
//this is referring to object car
var type = this.make + " " + this.model;
return type;
}
}
We can assign
var type = car.make + " " + car.model
If we do that, what if there is global variable car somewhere else in the scope chain and now our function will return something weird, hence, creating bugs. The this keyword not only refers to the object but it also contains the value of the object.
All functions in JavaScript have properties, like objects have properties. And when a function executes, it gets the this property—a variable with the value of the object that invokes the function where this is used. this reference ALWAYS refers to (and holds the value of) a singular object—and it is usually used inside a function or a method, although it can be used outside a function in the global scope. Note that when we use strict mode, this holds the value of undefined in global functions and in anonymous functions that are not bound to any object. That is why I hate using anonymous functions, but that is a separate topic altogether.
The Tricky Part
this refers to the object where it is defined, it is not until an object invokes the this Function that this is actually assigned a value. And the value it is assigned is based exclusively on the object that invokes the this Function. this has the value of the invoking object.
var car = {
make: "Honda",
model: "Accord",
getCarType: function(){
//this is referring to object car
var type = this.make + " " + this.model;
return type;
}
}
//this refers to car since car is invoking the function call
car.getCarType(); // returns Honda Accord
Using this inside the Global Scope
In the global scope, when the code is executing in the browser, all global variables and functions are defined on the window object. Therefore, when we use this in a global function, it refers to (and has the value of) the global window object (not in strict mode though, as noted earlier) that is the main container of the entire JavaScript application or web page.
var make = "Toyota";
var model = "Corolla";
function getCarType(){
//this is referring to object car
var type = this.make + " " + this.model;
return type;
}
var car = {
make: "Honda",
model: "Accord",
getCarType: function(){
//this is referring to object car
var type = this.make + " " + this.model;
return type;
}
}
getCarType(); // returns Toyota Corolla
window.getCarType(); //returns Toyota Corolla
car.getCarType(); //returns Honda Accord
Using this inside of a Closure
var car = {
make: "Honda",
model: "Accord",
getCarType: function(){
//this is referring to object car
var type = this.make + " " + this.model;
return type;
}
}
car.getCarType(); //Honda Accord
var anotherCar = {
make: "Honda",
model: "Pilot"
}
//We can use the apply method to set the "this" value explicitly
car.getCarType.apply(anotherCar); //Honda Pilot
The object that invokes the this Function is in context, and we can change the context by invoking the this Function with another object. Then this new object is in context.
var person = {
firstName: "Hien",
lastName: "Nguyen",
data: [
{ name: "Mickey Mouse"}, { name: "Donald Ducks" }
],
clickHandler: function(){
this.data.forEach(function(person){
//Creating a closure with this anonymous function
console.log(person.name); //Mickey Mouse, Donald Ducks
console.log(this.firstName); //error "undefined"
})
}
}
person.clickHandler();
When creating the closure, the inner function cannot access the outer function’s “this“, therefore resulting in an undefined error. How do we fix is?
var person = {
firstName: "Hien",
lastName: "Nguyen",
data: [
{ name: "Mickey Mouse"}, { name: "Donald Ducks" }
],
clickHandler: function(){
//set this value to another object before entering closure
var scope = this;
scope.data.forEach(function(person){
//Creating a closure with this anonymous function
console.log(person.name); //Mickey Mouse, Donald Ducks
console.log(scope.firstName); //Hien
})
}
}
person.clickHandler();
Using this when used in a method passed as a callback
var car = {
make: "Honda",
model: "Accord",
getCarType: function(){
//this is referring to object car
var type = this.make + " " + this.model;
return type;
},
onButtonClicked: function(event){
var carType = "Car Type = " + this.getCarType();
alert(carType);
}
}
$('#typeButton').click(car.onButtonClicked);
//What do you think will happen?
Okay, so what will happen is that clicking on the typeButton will yield an error. Basically, the Jquery typeButton is its own object and you are trying to invoke car.onButtonClicked function on a Jquery button object which has no context of car. It’s like trying to tell Joe to play volleyball but Jeff is the one that plays volleyball. So, now you see the problem, how should we fix it?
$('#typeButton').click(car.onButtonClicked.bind(car));
Since we really want to refer to the data property on the car object, we can use the Bind (), Apply (), or Call () method to specifically set the value of this.