This keyword in JavaScript
If you want to understand the meaning and importance of this
keyword in JavaScript just from the start, you need to ask the right questions and avoid using the wrong ones. If you want to ask something like “so what exactly this keyword means”, I will interfere abruptly and point out that this a wrong question to ask. If, on the other hand, you ask “so what exactly does this keyword mean in this particular context or environment, or scope”, I would say that you’re on a right track. And once you add “Is this keyword dynamic or static?” I would fear that I have nothing left to surprise you. And once you understand and get hang of this conventional trick in programming, you can extract all the perks it provides. Now it’s time to make a transition from philosophical, conceptual statements to more logical and practical examples.
In order to gradually understand and implement, we should take a look at how this
works in different environments. Let’s start here with the global scope, also known as the window object. To prove that when this
is used in global scope it refers to the window object, we can always ask in the console log of our browser, which has built-in JavaScript.
this === window // true
this === globalThis // true
This code proves, that when it’s called in a global environment, it points to the global object or window. With an understanding that has different meanings depending on where it’s called, let’s move forward and now try to invoke it in the function body, or function scope. Let’s try simple and gradually add functionality.
function testThis(){
console.log(this === window) // true
}
testThis()
We see in the example above, that even if inside of the function, if not explicitly told to point out to some other object, this
still refers to window
. But this statement is half true while when we try to invoke this
in strict mode, it will returnfalse
, while it’s more strict, apparently, and in strict mode the value of this
inside of a function is undefined
.
'use strict'
function testThis(){
console.log(this === window) // false
}
testThis()
With this in mind, we can move further and create a simple function where we can understand better the usage. We can now create a constructor function (with uppercase latter) and create its instances using new
keyword.
class Country {
constructor(name, capital, passenger) {
this.name = name.charAt(0).toUpperCase() + name.slice(1);
this.capital = capital.charAt(0).toUpperCase() + capital.slice(1);
this.passenger = passenger.charAt(0).toUpperCase() + passenger.slice(1);
this.sayHello = function() {
return `Hello, ${this.passenger}, welcome to the capital of ${this.name} ${this.capital}`;
}
}
}let france = new Country("france", "paris", 'john')
console.log(france.capital) // Paris
console.log(france.sayHello())
// Hello, John, welcome to the capital of France Paris
This a class function written in ES6, which is new syntactic sugar for a function. As we see in this example, this
doesn’t represent the global object, but whatever is passed as a function argument when an instance of the function is created using new
keyword (I wanted to enhance the user experience by adding some functionality when the passed argument in string format always returns its uppercased version, france
— France
. It’s not necessary but looks nicer).
sayHello()
is a method of an object and, when called, this
refers to the object itself. To prove this we can call typeof
this
inside of the sayHello()
function and will get object
. We can also store a reference to the function inside of another variable. For instance
let greet = france.sayHello();
console.log(greet)
Whenever we call greet
, it will point to the france
instance of Country
class function constructor. So we can say, that we store in this variable reference to function and later call this variable as a function.
Up to this moment, we implicitly bound this
to an object. However, we can explicitly attach this
to certain values using apply()
, call()
, and bind()
.
When a method is passed as a callback function, we lose the receiver and this
. To preserve this
from being lost, we can bind it to a specific object by using bind()
method.
function person(){
console.log(`${this.name} is ${this.age} years old`) // this becomes the object it is called on
}let john = {
name: "John",
age: 34
}person.call(john) // John is 34 years old
First of all, we create person
function which calls name
on this
. At this point, it doesn’t know what this
represents. Second, we create an object with key-value pairs stored in the john
variable. Third, we call()
this john variable-object on person
function thus binding this
to john
object. You can see how functions and variables are now connected and now this
is explicitly attached to the object it’s called upon. The same is genuinely true for apply()
. The only difference is that apply()
is used for arrays when call()
is used for a comma-separated list of arguments.
bind()
acts a bit differently. It allows us to permanently bind this
to a value. In the example below, we will create a function that is permanently bound to the object.
function person(){
console.log(`${this.name} is ${this.age} years old`) // this becomes the variable it is called on
}let john = {
name: "John",
age: 34
}person = person.bind(john)
person() // John is 34 years old
Thus we re-assign the person
function and it is now permanently bound to the variable.