My goal in writing this post is to promote a better understanding of this
keyword by sharing aspects of this
that I discovered while learning JavaScript. Skip the specifics and jump to the Cliffs Notes if you'd prefer.
Contents
Why this exists
In JavaScript, the this
keyword is a means to refer to the object or context that the code is currently executing within. In object-oriented programming, this
keyword makes it possible to reference the current object in callback functions and event handlers, allowing access to and manipulation of the current object's properties and methods. Additionally, this
enables programmers to build methods that preserve the context of the object they were called on while being reused at different instances.
The this
keyword can also be used to develop closures, inner functions that have access to the variables and information in their containing functions. It serves as a method to refer to the object whose property the function is, or to the object which is carrying out the current function.
How this works
In JavaScript, the value of this
keyword within a function is typically determined by how the function is invoked, not by the location of where the function was declared. Except for arrow functions, which have a different syntax and behaviour than regular functions.
The this
keyword can be passed to a function as an argument, or it can be implicitly set depending on the execution context.
The call(), apply(), and bind() methods can also be used to explicitly set the value of this
when a function is called. Each method can be used to call a function and modify how it is invoked, but they work in slightly different ways.
A few of the common factors that dictate how the this
keyword is determined in different contexts will be discussed next. Then I will discuss the function methods in greater detail.
What defines this
The context of the code that is presently running determines what the this
keyword represents. The call-site (the place in your code where a function is called) of the function is what determines the default behaviour of the this
keyword.
The following are often the most important factors to consider when defining this
keyword as a JavaScript programmer:
1st Factor
In the global scope, by default, this
refers to the global object, also known as the window
in the browser or global
in Node.js.
2nd Factor
In a regular function call, this
refers to the global object.
function myFunc() {
console.log(this);
}
myFunc(); // logs the global object (window or global)
3rd Factor
In a method call, this
refers to the object on which the method is defined.
let object = {
myMethod: function () {
console.log(this);
}
};
object.myMethod(); // logs object
4th Factor
In a constructor function, this
refers to the new object being constructed.
function MyConstructor() {
this.myProp = 'hello';
console.log(this);
}
let myObj = new MyConstructor(); // logs the new object, with a myProp property
5th Factor
When a function is called as a method of an object, this
refers to the object within the function.
// First Object
let object = {
myProp: 'Netflix',
getMethod: function() {
return this.myProp;
},
};
// Second Object
let otherObject = {
myProp: 'Disney'
};
object.getMethod(); // logs 'Netflix'
otherObject.getMethod (); // logs 'Disney'
console.log(object.getMethod()); // logs 'Netflix'
otherObject.getMethod = object.getMethod // logs f() { return this.myProp }
In the above code, the first object is called object
that has a myProp
property with a value of Netflix
, and a getMethod
method that returns the value of the myProp
property using this.myProp
.
When the object.getMethod()
method is called, this
inside the method refers to the object
on which the method is defined, so this.myProp
evaluates to object.myProp
which is Netflix
. Alternatively, getMethod()
can be called on the second object called otherObject
to return it's myProp
property as Disney
.
Clear as mud, right!?
The this
keyword is dynamic and its value is determined implicitly by the execution context of how the function is called.
However, for arrow functions, the value of this
is not dynamic, it's determined by the scope where the arrow function was defined. Instead of having its own this
keyword, the arrow function must access this
from the parent scope.
I plan on posting about arrow functions soon to better understand their atypical nature, so stay tuned!
Function methods make JavaScript more predictable by enabling the explicit definition of the value of the this
keyword. The call()
, apply()
, and bind()
methods allow you to explicitly specify the value of this
when a function is called. These methods modify how functions get invoked, but they work in slightly different ways.
The call()
and apply()
methods invoke the function immediately, while the bind()
method returns a new function with this
bound to a specified value. This new function can be called later.
Let's take a closer look!
Function Methods
The following Booking Flights code snippet is from Jonas Schmedtmann's The Complete JavaScript Course 2023, Section 10: A Closer Look at Functions, available on Udemy. It refers to booking flights with the second-largest European airline group and has one main object that I'll now discuss in greater detail on function methods.
// Booking Flights
// Main Object
const lufthansa = {
airline: 'Lufthansa',
iataCode: 'LH',
bookings: [],
// book: function() {}
book(flightNum, name) {
console.log(
`${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`
);
this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name });
},
};
lufthansa.book(239, 'Jonas Schmedtmann');
lufthansa.book(635, 'John Smith');
const eurowings = {
airline: 'Eurowings',
iataCode: 'EW',
bookings: [],
};
const book = lufthansa.book
book(23, 'Sarah Williams') // ERROR
const swiss = {
airlines: 'Swiss Air Lines',
iataCode: 'Lx',
bookings: [],
};
If you run this code, you will get the following output:
Jonas Schmedtmann booked a seat on Lufthansa flight LH239
John Smith booked a seat on Lufthansa flight LH635
The book
method of the lufthansa
object is called with the correct value for this
(i.e. this
refers to the lufthansa
object), so the bookings are added to the lufthansa
object's bookings
array.
However, when the book
function is assigned to the book
variable and then called with the book(23, 'Sarah Williams')
, the value of this
is determined by the way in which the function is called, and in this case it is called as a standalone function, not as a method of an object.
As a result, the value of this
is the global object (in a web browser, this is the window
object). This means that the airline
and iataCode
properties of this
object will not be defined, and you will see an error message
logged to the console similar to the following:
Uncaught TypeError: Cannot read property 'airline' of undefined
To fix this error, we need to use the call()
method on the book
function to properly set the value of this
keyword to the eurowings
object. As a result, we get the booking added to the eurowings
object's bookings
array.
Call Method
Using the call()
method effectively specifies the value of this
when calling the book
function, like below:
// Call Method
book.call(eurowings, 23, 'Sarah Williams');
console.log(eurowings); // logs Sarah Williams booked a seat on Eurowings flight EW23
book.call(lufthansa, 239, 'Mary Cooper');
console.log(lufthansa); // logs Mary Cooper booked a seat on Lufthansa flight LH239
In the above code, the book
function is defined as a method of the lufthansa
object, but the call()
method on the book
function can set the value of this
to the eurowings
object as well.
The call()
method allows you to invoke a function and specify the value of this
as well as any arguments that should be passed to the function when it is called, regardless of where the function is defined.
The value of the this
keyword is determined dynamically based on how a function is invoked. It is also possible to explicitly set the value of the this
keyword within a function by using the call()
method.
The call()
method allows you to specify the value of this
explicitly, rather than relying on the default behavior of the this
keyword. This can be useful when you want to reuse a function that was defined in one context, but use it in a different context where the value of this
needs to be different.
Apply Method
The apply()
method allows you to call a function with a specified value for this
, and an array of arguments. This can be useful when calling a function with a variable number of arguments.
For example, where flightData
is set to an array:
// Apply Method
const swiss = {
airline: 'Swiss',
iataCode: 'LX',
bookings: [],
book: function(flightNum, name) {
console.log(`${name} booked a seat on ${this.airline} flight${this.iataCode}${flightNum}`
);
this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name });
},
};
// Array as an argument
const.flightData = [583, 'Carlos Santana'];
book.apply(swiss, flightData);
console.log(swiss);
The following is logged to the console:
Carlos Santana booked a seat on Swiss flight LX583 { airline: 'Swiss', iataCode: 'LX', bookings: [{ flight: 'LX583', name: 'Carlos Santana' }], book: [Function]}
In the above code, the apply()
method is called on the book
function, and it is passed two arguments: the value of this
(the swiss
object) and an array of arguments (flightData
). The booking is then added to the swiss
objects bookings
array.
The apply()
method is less commonly used in modern JavaScript because there is a better way to accomplish the same task.
I'm referring to the spread operator (...
), which allows you to spread an array of values into individual arguments when calling a function. This can be a more concise and convenient way of calling a function with an array of arguments in comparison to the apply()
method.
For example, instead of using the apply()
method as shown in the previous example, you could use the spread operator like so:
// Spread Operator
const flightData = [583, 'Carlos Santana'];
book.call(swiss, ...flightData);
console.log(swiss); // logs Carlos Santana booked a seat on Swiss flight LX583
Invoking the book
function, by spreading the flightData
into separate arguments with the spread operator effectively adds Carlos Santana
to the swiss
objects bookings
array!
Because the spread operator is a relatively new addition to JavaScript, it may not be supported in all environments. It is, however, widely supported in modern browsers and JavaScript runtimes, making it a convenient and concise method of calling a function with an array of arguments.
Bind Method
The bind()
method creates a new function that, when called, has its this
keyword set to the provided value, that you specify. This new function can be called later.
By calling the bind()
method on the book
function and pass different values for this
, you can create new functions with fixed values for this
. For example:
// Bind Method
const bookEW = book.bind(eurowings);
const bookLH = book.bind(lufthansa);
const bookLX = book.bind(swiss);
The bind()
method returns a new function with the value of this
set to the specified value.
In the above example, the bind()
method is called on the book
function three times, each time with a different value for this
, resulting in three new functions:
-bookEW
, which has this
fixed to the eurowings
object
-bookLH
, which has this
fixed to the lufthansa
object
-bookLX
, which has this
fixed to the swiss
object.
These functions are identical to the book
function, except the value of this
is fixed to the value that was passed to bind()
. You can then call these functions at a later time, and the value of this
will be fixed to the value that you specified when you created the function with bind()
.
The following gets logged to the console:
bookEW(23, 'Steven Williams'); // Adds a booking to the eurowings object's bookings array
bookLH(654, 'Alice Smith'); // Adds a booking to the lufthansa object's booking array
bookLX(583, 'Bob Johnson'); // Adds a booking to the swiss object's bookings array
This can be useful when you want to reuse a function in different contexts, but you want the value of this
to be different in each context. The bind
method allows you to create new functions with fixed values for this
, which can be called at a later time. For example:
// Calling a single function for additional flight bookings
const bookEW23 = book.bind(eurowings, 23);
bookEW23('Jonas Schmedtmann');
bookEW23('Martha Stewart');
The bind()
method in this example, was used to create new functions with the this
value set to either lufthansa
or eurowings
, depending on which object is passed as an argument to bind()
.
The bookEW
function is created with the this
value set to eurowings
, so when it is called with (23, 'Steven Williams')
, it logs the message: "Steven Williams booked a seat on Eurowings flight EW23"
.
The bookEW23
function is also created with the this
value set to eurowings
, but it is already partially applied with the first argument set to 23
. When it is called with a single argument 'Jonas Schmedtmann'
, it logs the message: "Jonas Schmedtmann booked a seat on Eurowings flight EW23"
.
When you create a new function using the bind()
method and partially apply some of its arguments, the resulting function will only have placeholders for the remaining arguments.
Partial application refers to the process of creating a new function by fixing some of the arguments of an existing function. The new function is then called with the remaining arguments.
For example, in the case of the bookEW23
function, the flightNum
argument is already set to 23
, so when you call bookEW23
function, you only need to provide the name
argument. The resulting function call will look like this: bookEW23(name)
.
You can also use the bind()
method to set the value of multiple arguments at once. For example:
const bookEW23Martha = book.bind(eurowings, 23, 'Martha');
bookEW23Martha(); // logs Martha booked a seat on Eurowings flight EW23
Functional programmers often use partial application to code reusable, configurable functions that are simple to adapt for particular use cases. The bind()
method is one way to implement partial application in JavaScript, but there are other ways as well, such as using higher-order functions or creating a function that returns a partially applied function.
Cliffs Notes
The call()
, apply()
, and bind()
methods are all ways to specify the value of this
keyword when calling a function.
The call()
method allows you to call a function with a specified value for this
, and pass any number of arguments to the function as separate parameters.
The apply()
method allows you to call a function with a specified value for this
, and pass any number of arguments to the function as an array.
The bind()
method allows you to create a new function with a specified value for this
, and optionally set some or all of the arguments to the new function. The new function can then be called at a later time, with the remaining arguments passed when the function is called.
These methods can be useful when you want to reuse a function in different contexts, but you want the value of this
or the arguments to be different in each context. They allow you to specify the value of this
and the arguments explicitly, rather than relying on the default behavior of the this
keyword and the arguments.
Thanks for reading and hopefully this was helpful! I welcome constructive feedback, and if you find an error, please kindly let me know. Disclaimer: This post was produced with the help of artificial intelligence.
Top comments (0)