In C++, this
keyword usually refer to the object ifself. It always uses in the method of class. And there are no any tricks with this
keyword. But in Javascript, this
keyword is affected by many things such as context of object, the usage of functions, …
So, in this article, we will discuss about this
keyword to make our concern more clear about this
keyword.
Table of contents
Introduction to this keyword
According to the w3school.com, we have:
- The JavaScript
this
keyword refers to the object it belongs to. - It has different values depending on where it is used:
- In a method,
this
refers to the owner object. - Alone,
this
refers to the global object. - In a function,
this
refers to the global object. - In a function, in strict mode,
this
is undefined. - In an event,
this
refers to the element that received the event. - Methods like call(), and apply() can refer
this
to any object.
- In a method,
Simple rules for this keyword
So, with the previous section, we will have a rule:
this keyword will refer to the object or context that is the nearest to it.
Examples for this keyword
-
Regular function
function foo() { console.log("Simple function call"); console.log(this === window); } foo()
Because,
foo()
is a regular function, so thethis
will refer to the global object -window
.Result:
Simple function call true
But if we use strict mode, we have:
function foo() { 'use strict'; console.log("Simple function call"); console.log(this === window); } foo()
So, we have a result will look something like;
Simple function call false
Now,
this
will refer toundefined
, notwindow
object. -
Constructor function
function Person(first_name, last_name) { this.first_name = first_name; this.last_name = last_name; this.displayName = function() { console.log(`Name: ${this.first_name} ${this.last_name}`); } } let john = new Person('John', 'Reid'); john.displayName();
When we call
new
onPerson
constructor function, Javascript will create a new object inside thePerson
constructor function and save it asthis
. Then, thefirst_name
,last_name
anddisplayName
properties will be added on the newly created this object. -
Simple object
function simpleFunction () { console.log("Simple function call") console.log(this === window); } let user = { count: 10; simpleFunction: simpleFunction, anotherFunction: function() { console.log(this === window); } };
When we call
user.simpleFunction()
oruser.anotherFunction()
, we have a result:Simple function call false
Because
this
now refer to theuser
object.But if we do something like:
let ourFunction = user.anotherFunction(); ourFunction();
Then, we have:
Simple function call true
Because
ourFunction()
is a regular function, sothis
will refer to the global object - window. -
Embedded regular function into method class
var john = { name: 'john', yearOfBirth: 1990, calculateAge: function() { console.log(this); console.log(2016 - this.yearOfBirth); function innerFunction() { console.log(this); } innerFunction(); } }
When we call
john.calculateAge()
, thethis
will be implicitly passed into a register. So,calculateAge()
function is called, CPU will get value form that register and assign to thethis
keyword such asthis
,this.yearOfBirth
`.But, inside the
innerFunction()
function,this
will not refer to thejohn
object, mainly because it is a regular function. Therefore,this
ininnerFunction()
will refer to global object -window
. -
Use arrow function
Unlike regular function, arrow functions do not get their own
this
keyword. They simply use thethis
keyword of the function they are written in.var box = { color: 'green', // 1 position: 1, // 2 clickMe: function() { // 3 document.querySelector('body').addEventListener('click', function() { var str = 'This is box number ' + this.position + ' and it is ' + this.color; // 4 alert(str); }); } }
When we call
box.clickMe()
method, a result we have:This is box number undefined and it is undefined
Because inside the
clickMe()
function,this
keyword also is passed to it. But in callback function,this
keyword ofbox
object do not pass. So,this
in callback function will beundefined
`.To solve this problem, we need to save the
this
keyword ofbox
object into the other variable ofclickMe()
function.var box = { color: 'green', // 1 position: 1, // 2 clickMe: function() { // 3 let self = this; document.querySelector('body').addEventListener('click', function() { var str = 'This is box number ' + self.position + ' and it is ' + self.color; // 4 alert(str); }); } }
So, we have:
This is box number 1 and it is green
Another solution is to use arrow function.
var box = { color: 'green', // 1 position: 1, // 2 clickMe: function() { // 3 document.querySelector('body').addEventListener('click', () => { var str = 'This is box number ' + this.position + ' and it is ' + this.color; // 4 alert(str); }); } }
The amazing thing about arrow functions is that they share the lexical
this
keyword of their surroundings.Then, we have:
This is box number 1 and it is green
And we still have other case for arrow function such ash.
var box = { color: 'green', // 1 position: 1, // 2 clickMe: () => { // 3 document.querySelector('body').addEventListener('click', () => { var str = 'This is box number ' + this.position + ' and it is ' + this.color; // 4 alert(str); }); } }
When we have
box.clickMe()
, we have:This is box number undefined and it is undefined
The
this
keyword of the click event listener’s closure shares the value of thethis
keyword of its surroundings. Its surroundings in this case is the arrow functionclickMe()
. Thethis
keyword of theclickMe
arrow function refers to the global object, in this case thewindow
object.So,
this.position
andthis.color
will beundefined
because ourwindow
object does not know anything about theposition
or thecolor
properties. -
Some ways to pass
this
inmap
functionCome back to the previous example, we have:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.displayName = function() { console.log(`Name: ${this.firstName} ${this.lastName}`); } } Person.prototype.myFriends = function(friends) { var arr = friends.map(function(friend) { return this.firstName + ' is friends with ' + friend; }); console.log(arr); } let john = new Person("John", "Watson");
So, call
john.myFriends(["Emma", "Tom"])
we have a result:"undefined is friends with Emma", "undefined is friends with Tom"
Because
this
in callback function ofmap
will refer to the global object -window
.To fix this problem, we have 3 solutions:
-
Save
this
keyword inside the other variable inmyFriends()
function.Person.prototype.myFriends = function(friends) { let self = this; var arr = friends.map(function(friend) { return self.firstName + ' is friends with ' + friend; }); console.log(arr); }
-
Using
bind
on themap
function’s closure.Person.prototype.myFriends = function(friends) { var arr = friends.map(function(friend) { return this.firstName + ' is friends with ' + friend; }.bind(this)); console.log(arr); }
Calling
bind
will return a new copy of themap
callback function but with athis
keyword mapped to the outerthis
keyword, which is, in this case, will be thethis
keyword referring to the object callingmyFriends
. -
Using arrow function for map function’s closure
Person.prototype.myFriends = function(friends) { var arr = friends.map(friend => { `${this.firstName} is friends with ${friend}`; }); console.log(arr); }
-
Wrapping up
- In regular function,
this
refer to global object. - Arrow function will retain the
this
keyword of outer function.
Thanks for your reading.
Refer:
https://itnext.io/the-this-keyword-in-javascript-demystified-c389c92de26d