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
thiskeyword refers to the object it belongs to. - It has different values depending on where it is used:
- In a method,
thisrefers to the owner object. - Alone,
thisrefers to the global object. - In a function,
thisrefers to the global object. - In a function, in strict mode,
thisis undefined. - In an event,
thisrefers to the element that received the event. - Methods like call(), and apply() can refer
thisto 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 thethiswill refer to the global object -window.Result:
Simple function call trueBut 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 falseNow,
thiswill refer toundefined, notwindowobject. -
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
newonPersonconstructor function, Javascript will create a new object inside thePersonconstructor function and save it asthis. Then, thefirst_name,last_nameanddisplayNameproperties 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 falseBecause
thisnow refer to theuserobject.But if we do something like:
let ourFunction = user.anotherFunction(); ourFunction();Then, we have:
Simple function call trueBecause
ourFunction()is a regular function, sothiswill 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(), thethiswill be implicitly passed into a register. So,calculateAge()function is called, CPU will get value form that register and assign to thethiskeyword such asthis,this.yearOfBirth`.But, inside the
innerFunction()function,thiswill not refer to thejohnobject, mainly because it is a regular function. Therefore,thisininnerFunction()will refer to global object -window. -
Use arrow function
Unlike regular function, arrow functions do not get their own
thiskeyword. They simply use thethiskeyword 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 undefinedBecause inside the
clickMe()function,thiskeyword also is passed to it. But in callback function,thiskeyword ofboxobject do not pass. So,thisin callback function will beundefined`.To solve this problem, we need to save the
thiskeyword ofboxobject 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 greenAnother 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
thiskeyword of their surroundings.Then, we have:
This is box number 1 and it is greenAnd 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 undefinedThe
thiskeyword of the click event listener’s closure shares the value of thethiskeyword of its surroundings. Its surroundings in this case is the arrow functionclickMe(). Thethiskeyword of theclickMearrow function refers to the global object, in this case thewindowobject.So,
this.positionandthis.colorwill beundefinedbecause ourwindowobject does not know anything about thepositionor thecolorproperties. -
Some ways to pass
thisinmapfunctionCome 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
thisin callback function ofmapwill refer to the global object -window.To fix this problem, we have 3 solutions:
-
Save
thiskeyword 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
bindon themapfunction’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
bindwill return a new copy of themapcallback function but with athiskeyword mapped to the outerthiskeyword, which is, in this case, will be thethiskeyword 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,
thisrefer to global object. - Arrow function will retain the
thiskeyword of outer function.
Thanks for your reading.
Refer:
https://itnext.io/the-this-keyword-in-javascript-demystified-c389c92de26d