The this
keyword in JavaScript can be quite confusing. Unlike languages like Java, where this
works in a more straightforward way, JavaScript requires us to understand the context in which this
is used, as it doesn't always point to the object we expect.
Let's break down the this
keyword to get a clear understanding of how it works. Knowing how this
behaves in different scenarios is a crucial skill for any JavaScript developer.
There are five main rules that govern the behavior of this
in JavaScript:
- Implicit Binding
- Explicit Binding
- New Binding
- Lexical Binding
- Window Binding
1. Implicit Binding
The implicit binding is perhaps the most common scenario we will encounter when writing our JavaScript applications. The simple rule here is to look to the left of the dot when the function is invoked. Whenever we see a dot (.
), we can look to the left to find the object that this
is referring to.
When a function is called as a method of an object, this
refers to the object that the method is called on.
In this example, this
inside greet
refers to the person
object because greet
is called as a method of person
.
2. Explicit Binding
Explicit binding means we tell the JavaScript engine exactly what this
should refer to in a given context.
Consider this function using the this
keyword:
Here, this
is not set, so the output is incomplete. We can explicitly set this
using call
, apply
, or bind
.
In this example, we use the call()
method to set this
to the person
object, making this.name
refer to person.name
.
The bind()
and apply()
methods are used similarly but in different scenarios. For instance, bind()
is useful for maintaining the correct this
context when working with functions in JavaScript.
- We define a
user
object with aname
and agreet
method. - Calling
greet
directly on theuser
object works as expected. - Assigning
greet
to a variable and calling it loses thethis
context, resulting inundefined
. - Using
.bind(user)
, we create a new function withthis
bound touser
. - We show how
.bind()
preserves the correctthis
context withsetTimeout()
.
This demonstrates how .bind()
helps maintain the correct this
context, especially when dealing with callbacks or event handlers.
Without .bind()
, functions saved in variables lose their this
reference.
Using the .apply()
method is similar to the call()
but it takes arguments as a list.
You can see that we take in two arguments greeting
and punctuation
but when calling the function we can simply use the the .apply()
,
where the first argument is the this
and the second argument is the list with the provided arguments.
By understanding and using call()
, apply()
, and bind()
, we can explicitly set the this context in JavaScript,
ensuring our functions behave as expected in different situations.
One tip that I like to use to remind myself of the difference between call()
and apply()
is that call()
is for comma-separated arguments,
while apply()
is for array-like arguments where a in apply()
stands for array.
3. New Binding
When a function is used as a constructor with the new
keyword, this
refers to the newly created instance.
Here we create a new instance of the Person
constructor function using the new
keyword.
Inside the Person
function, this
refers to the new instance being created,
allowing us to set properties on it.
When we use the new
keyword, JavaScript creates a new object and sets this
to that object. This is known as new binding.
4. Lexical Binding
Arrow functions use lexical scoping for this
, which means this
keyword is determined by the surrounding context.
We often take advantage of using the arrow functions to avoid the confusion of the this
keyword.
Since arrow functions don't have their own this
context, they inherit the this
value from the enclosing function.
Here we can see that we get undefined
because the arrow function doesn't have its own this
context and it inherits the this
value from the global context.
But to really see where arrow functions shine, let's look at the following example:
Now we can see that the arrow function inside the setTimeout
function is able to access the this
value from the greet
function.
Since the greet
function is a regular function and has its own this
context,
the arrow function inside it can access the this
value from the greet
function.
This is a common pattern in JavaScript, where we use arrow functions to maintain the correct this
context in nested functions.
5. Window Binding
If none of the above rules apply, this
defaults to the global object, which is window
in the browser and global
in Node.js.
What I mean by none of the above rules apply is when we have a function that is not a method of an object, not a constructor function, and not an arrow function.
As we know the previous rules it prints out nothing unexpected because the this
value is not set and it defaults to the global object.
But if we are in strict mode, the this
value will be undefined
instead of the global object.
In strict mode, this
is undefined
when the function is not called as a method of an object.
But if we set the window
object to the this
value, we can access the name
property.
Now we can see that we are able to access the name
property from the window
object.
Since the greeet()
function is not a method of an object, it defaults to the global object where looks at the window
object.
Conclusion
Understanding the this
keyword in JavaScript is crucial for writing clean and maintainable code. By following the rules of implicit, explicit, new, lexical, and window binding, we can ensure that this
behaves as expected in different scenarios.
Remember to always consider the context in which this
is used and apply the appropriate binding to avoid unexpected behavior in your JavaScript applications.
I hope this article has helped you understand the this
keyword in JavaScript better.
Happy coding!