Specifics of use this keyword in arrow functions — JS: Introduction to Object Oriented Programming (OOP)
The fundamental distinction between them is evident in how they deal with context. In brief, the context of normal functions is determined by where they are called, but the context of arrow functions is determined by where they are defined.
Let's look at a few examples. The simplest case is the definition of a function at the module level. In this case, the context of the call will be the module itself.
Note: the examples below are for Node.js. In browsers, this
will, by default, contain a global Window object.
const f1 = () => { // arrow function
console.log(this);
};
f1(); // undefined
function f2() { // normal function
console.log(this);
}
f2(); // undefined
The functions' behavior doesn't differ here, because the context of both function calls is the module itself and this
isn't defined for modules in ES6. Now let's try to add these functions to the object:
const obj = {
f1, f2,
};
obj.f1(); // undefined
obj.f2(); // { f1: [Function: f1], f2: [Function: f2] }
A normal function is expected to be associated with the context of the object it's called on. But in the arrow function's case, this didn't happen. Why? An arrow function does not have its own context; it is related to the lexical environment, that is, the function within which the arrow function is declared. This is a critical point. Nothing else but the top-level function determines the context of the arrow function. This behavior cannot be altered using the call
or bind
functions.
f1.call({ name: 'hexlet' }); // undefined
f1.bind({ name: 'hexlet' })(); // undefined
Now let's define an arrow function inside an object and try to call it:
const company = {
f1: () => { // arrow function
console.log(this);
},
f2() { // normal function
console.log(this);
},
};
company.f1(); // undefined
company.f2(); // { f1: [Function: f1], f2: [Function: f2] }
Here we see exactly the same thing. Even though the arrow function is defined inside an object and is called from the same object, the context is still related to where the function is defined (the lexical environment) – and that's the module itself.
Now let's try to define an arrow function within another function:
const printer = {
items: [1],
print() { // it's important that the external function has context
// The arrow function is defined inside the print function,
// but is called inside the forEach method
this.items.forEach(() => console.log(this.items));
},
};
printer.print(); // [1]
The arrow function seems to have understood this
, but this context doesn't belong to the function, it's borrowed it from the external function, print
. To better understand this, imagine how the arrow function inside forEach
is called. It's called directly, not from an object. Normal functions create their own context in this situation, but arrow functions don't. They take context from where they were defined. The arrow function is defined in the print
, function, so it takes the context of the print
function.
The exact same code will not work with a normal function:
const printer = {
items: [1],
print() {
this.items.forEach(function () { console.log(this.items); });
},
};
printer.print(); // undefined
This is precisely because the function is called as a normal function, not a method. In this case, its context is an empty object, which means this.items
will return undefined
.
Where might all this be needed? In the vast majority of situations, we don't need this
inside arrow functions at all. It is always better to work with data passed explicitly. However, in some circumstances, this characteristic of arrow functions can help to simplify the code. In some cases, a higher-order function is called within an object method, and the arrow function that works with this
is provided to it.
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.