let person = {
name: "Joe"
};
// Outside of a function, 'this' is undefined.
// The arrow function takes 'this' from the surrounding scope
// - which is here - so it is undefined.
person.arrowFunc = () => console.log(`'this' is ${this}`);
// Logs: 'this' is undefined
person.arrowFunc();
Normally you'd expect person.arrowFunc()
to call arrowFunc()
with this
set to person
. However arrow functions override that rule and use this
from their outer scope, in this case setting this
to undefined
.
At first glance this might seem like an odd exception to the rule, but it does come in useful in practice. It's useful for using functions inside methods when you want to re-use the same value of this
, in particular when using the callback pattern.
To illustrate this let's take a quick detour through a click event. Browsers provide a global document
object representing the HTML document for the page. Many browser objects also have an addEventListener
method to handle events. This method accepts a function that will be called by the browser when the given event happens, also known as a callback. So we can use the following code to detect whenever the user clicks their mouse anywhere in the page.
document.addEventListener("click", function ()
{
console.log("Click event!");
});
When previewing this, click inside the preview window, and then check the browser console. You should see it log Click event! as the browser calls the function every time the "click" event happens anywhere in the page.
Now try moving this code to a method and add a reference to this
. It won't work as expected, because this
does not refer to the person object.
let person = {
name: "Joe",
initialize()
{
document.addEventListener("click", function ()
{
console.log(`Click event! The person's name is ${this.name}`);
});
}
}
// Call initialize() to add the click event listener
person.initialize();
Now when you click in the preview window, it will log Click event! The person's name is undefined, which is not what we wanted. The reason is the browser is calling the "click" event function without a reference to the person object.
One way to solve this is to take advantage of a closure, which we covered in part 6. A variable can be added to the initialize()
function that remembers the value of this
. Then the "click" function can refer to that variable instead.
let person = {
name: "Joe",
initialize()
{
// Save the value of 'this' when it refers to the right object
let savedThis = this;
document.addEventListener("click", function ()
{
// Refer to 'savedThis' instead of 'this'
console.log(`Click event! The person's name is ${savedThis.name}`);
});
}
}
person.initialize();
It now works as expected: when we click in the page, it logs Click event! The person's name is Joe.
However we can take advantage of the fact arrow functions take the value of this
from their outside scope, just like with savedThis
in the example above. Then we can refer to this
as normal without having to have a different variable.
let person = {
name: "Joe",
initialize()
{
// Use an arrow function, which remembers 'this'
document.addEventListener("click", () =>
{
console.log(`Click event! The person's name is ${this.name}`);
});
}
}
person.initialize();
This works the same as before, correctly logging the name Joe. It demonstrates how arrow functions are useful both for their shorter syntax, and for using the value of this
from the outer scope. This kind of callback pattern is very common in practice, so this feature of arrow functions remembering this
is actually a useful thing to take advantage of.