Register to get access to free programming courses with interactive exercises

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.

About Hexlet learning process

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.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.