A Closer Look at the 'this' keyword in JavaScript
Explore the 'this' keyword in JavaScript: its behavior in global scope, functions, methods, and callbacks, with examples
The this
keyword is a unique identifier in JavaScript. The value of this
is determined at runtime and can vary depending where you are using it. As a result, the behavior of the this
keyword seems confusing to many people, especially to those who are new to the language or have limited experience with its nuances.
In other languages, e.g. C++, Java, C# etc, the
this
keyword represents the current instance of a class, But JavaScript'sthis
keyword is a bit different in nature. So it's better not to compare with how it works in other languages.
This article aims to explore the value of the this
keyword in different contexts. Let's dive in and unveil the mysteries of the this
keyword!
this
in global scope
When we use the this
keyword in the global scope (i.e. outside any function), it's value refers to the global
object.
Global object
The global
object is the top-level object in the scope chain and acts as a container for all global variables (defined with the var
keyword), functions, and objects.
In the browser environment, the global object is the window
object. It provides access to various properties and methods related to the browser environment.
console.log(this); // the window object
console.log(window); // same as previous statement
Global functions like console.log
, parseInt
, setTimeout
or global objects like document
, Date
etc. are properties of the window
object.
console.log(console === window.console); // true
console.log(setTimeout === window.setTimeout); // true
console.log(document === window.document); // true
In Node.js environment, the global object is global
object. It sounds strange as a sentence, but the identifier of the object itself is global
. It's similar to how the window
object is used in browsers but designed for server-side JavaScript execution.
console.log(this === global); // true
console.log(this === global.setTimeout); // true
this
inside Functions
When a function is called, the value of the this
keyword refers to the global
object in non-strict mode, or undefined
in strict mode.
// using the var keyword, so that the variable
// firstName gets attached with the global object
var firstName = "Naimul";
function sayHello() {
console.log(`Hello, my name is ${this.firstName}`);
}
sayHello(); // Hello, my name is Naimul
The same piece of code when run in
strict mode
, It will throw an error “Cannot read property ‘name’ of undefined”. It’s because the value ofthis
inside the function becomesundefined
instead of referring to theglobal
object.
Explicit function binding
Sometimes we need to explicitly bind the this
keyword to a specific object to ensure that the function operates with the correct this
value, regardless of how it is invoked.
We have three methods in JavaScript to accomplish this: call
, apply
, and bind
, which we may call on any function. When a function is invoked with the call
, apply
, or bind
, the this
keyword will refer to what we pass in as the first parameter.
Function.prototype.call()
In the following example, we’re explicitly binding the this
keyword to the person
object. It takes the following input parameters:
thisArg
: The value to be passed as the this context within the function.arg1
,arg2
, ...: Optional arguments to be passed to the function individually.
const person = {
firstName: "Naimul"
};
function sayHello(greet) {
console.log(this === person); // true
console.log(`${greet}, my name is ${this.firstName}`);
}
sayHello(); // Hi, my name is undefined
sayHello.call(person, "Hi"); // Hi, my name is Naimul
In this example, the sayHello
function is called with the call
method, which binds the this
to the person
object, so the output is "Hi, my name is Naimul".
Function.prototype.apply()
The apply
method is another method that allows us to call a function and explicitly set the this
value. However, unlike call
, apply
takes arguments as an array or an array-like object. It takes the following input parameters:
thisArg
: The value to be passed as the this context within the function.[arg1, arg2, ...]
: An array containing the arguments to be passed to the function.
const person = {
firstName: "Naimul"
};
function sayHello(greet) {
console.log(`${greet}, my name is ${this.firstName}`);
}
sayHello.apply(person, ["Hi"]); // Hi, my name is Naimul
Function.prototype.bind()
Unlike call
and apply
, bind
does not execute the function immediately but instead returns a new function where the this
context is fixed to a specified object.
const person = {
firstName: "Naimul"
};
function sayHello(greet) {
console.log(`${greet}, my name is ${this.firstName}`);
}
const sayHelloWithPerson = sayHello.bind(person, "Hola");
sayHelloWithPerson();
Note that the
greet
parameter is pre-filled with "Hello". Even if we invoke it with a different greet value,sayHelloWithPerson("Hello")
, it won’t use the new value.
this
inside methods
When a method is called on an object in JavaScript, the this
keyword inside that method refers to the object itself. This behavior is known as method binding and it's a fundamental aspect of how objects works in JavaScript. Let's look at the following example
const person = {
firstName: "Naimul",
greet: function() {
// `this` refers to the object itself - the `person` object
console.log(`Hello, my name is ${this.firstName}`);
},
};
person.greet(); // "Hello, my name is Naimul"
What happens when we use arrow functions instead of regular functions? Let’s find out!
const person = {
firstName: "Naimul",
greet: () => {
// `this` refers to the global object
console.log(`Hello, my name is ${this.firstName}`);
},
};
person.greet(); // "Hello, my name is undefined"
Arrow functions behave differently than regular functions. The arrow function printName
captures the this
value from the surrounding scope, which, in this case, is the global scope (the outermost scope).
It’s generally recommended to use regular function expressions or function statements. It’s because you need to access the object’s properties or other methods.
Nested function
What happens if we have nested functions within a method? Let’s move the console.log
in a nested function and call the function within the method. Trust me! We'll find out something very interesting.
const person = {
firstName: "Naimul",
greet: function () {
function showGreetingMessage() {
// `this` refers to the `global` object
console.log(`Hello, my name is ${this.fullName}`);
}
showGreetingMessage();
},
};
person.greet(); // "Hello, my name is undefined"
The value of this
in the showGreetingMessage()
function is the global
object, because it is being invoked as a standalone function and not as a method of any object. It does not inherit the this
value of the outer scope. To fix this issue, we can take several approach
Storing the
this
variable in aself
variable, and useself
inside the functionCalling the
showGreetingMessage
function withcall
,bind
orapply
Switching to arrow functions
To fix this, we’ll convert our regular functions into arrow functions.
const anotherPerson = {
fullName: "John Doe",
greet: function () {
const showGreetingMessage = () => {
console.log(`Hello, my name is ${this.fullName}`);
};
showGreetingMessage();
},
};
anotherPerson.greet();
By switching from regular functions to arrow functions, the this
value is no longer redefined within the function, and instead, it inherits the this
value from the surrounding myFriend object. This allows the arrow functions to access the correct this
value, which is the myFriend object, and fix the issue with this
not pointing to the right object.
The topic of arrow functions is broad and can require its own in-depth exploration. In a future article, we can dive deeper into advanced concepts related to arrow functions and best practices for their usage in different scenarios.
Constructor functions
When a function is called as a constructor with the new
keyword, this
is bound to the newly created object. For example:
// `this` refers to the newly created object
// which is returned by the new Person() call
function Person(name, age) {
this.name = name;
this.age = age;
}
const me = new Person("Naimul", 26);
console.log(me); // Person { name: 'Naimul', age: 26 }
this
inside callback functions
Callback functions are functions that you pass to another function as an argument, so that they can be executed after some async operation (most of the time, but not always). Let’s try to assume what will be the output of the following function
function Person(name, age) {
this.name = name;
this.age = age;
setTimeout(function() {
console.log(this); // Window {...}
}, 1000);
}
const me = new Person("Naimul", 26);
To fix this you can either explicitly bind the function to the proper this
value or you can use arrow functions.
function Person(name, age) {
this.name = name;
this.age = age;
setTimeout(() => {
console.log(this); // Person { name: 'Naimul', age: 26 }
}, 1000);
}
const me = new Person("Naimul", 26);
In conclusion, I hope this article has shed some light on the this
keyword and now you understand it clearly in various contexts.