Тема о многих аспектах определения значения this — традиционных функциях и функциях-конструкторах, а также наследовании.

Пример 1

function foo() {
    this.total = 0;
    setTimeout(function baz() {
        this.total++;
        console.log(`in the scope of baz, ${this.total} is NaN.`);
    });
    setTimeout(() => console.log(`foo total remains ${this.total}`));
    this.total.subtotal = 0;
} 
let instance = new foo();

this.total равен NaN при вызове в рамках функции baz(). this.total увеличивается внутри функции setTimeout, а this получается из глобального объекта окна.

setTimeout(function, milliseconds) {
    fn() // this is window.fn(), implicitly
}

Традиционные функции заботятся о том, где они вызываются, как видно при добавлении к функции-конструктору путем объявления методов-прототипов даже за пределами ее области действия.

foo.prototype.increment = function () {
    this.total.subtotal++;
}
foo.prototype.increment = () => {
    this.total.subtotal++;
}

Контекстно-зависимая стрелочная функция получает и вызывает неопределенное свойство .total элемента this из глобальной области, в которой оно объявлено. Как мы знаем, атрибут [object Window] this.total не определен! Вы получаете TypeError: Cannot read properties of undefined (чтение «value»).

Пример 2

function foo(){
    this.total = 0;
    setTimeout(() => {
        this.total++;
        console.log(`in the scope of foo, this.total is ${this.total}`);
    }); 
}
new foo();

При ссылке на this традиционные функции заботятся о том, где они вызываются, тогда как стрелочные функции заботятся о том, где они объявлены.¹ Вот почему мы используем обработчики событий в виде стрелочных функций; нет необходимости в этой привязке. Для традиционных функций, которые можно использовать в качестве функций-конструкторов, мы можем использовать оператор new. Это скрытое свойство [[Construct]], которое связывает традиционные функции с оператором new². Важно отличать =› от конструкторов.

const foo = () => {};  
console.log(`foo prototype is ${foo.prototype}`); 
new foo();

TypeError: foo is not a constructor.

Если вам интересно, как получить такое причудливое форматирование в Visual Studio Code, Shift + ⌥ + F автоматически форматирует.

Дополнительную информацию по этой теме см. в разделе freeCodeCamp, когда и почему следует использовать стрелочные функции ES6!³

[1]: Мой друг Джастин Кристенсен говорит, что мы должны использовать стрелочные функции, которые ведут себя так, как ожидается.

[2]: Джон Ау-Юнг. Лучшее из современного JavaScript — стрелочные функции и новые возможности ООП
https://javascript.plainenglish.io/best-of-modern-javascript-arrow-functions-and-new-oop -особенности-6ce77ce9db8e

[3]: freeCodeCamp. Когда (и почему) следует использовать стрелочные функции ES6, а когда не следует
https://www.freecodecamp.org/news/when-and-why-you- следует-использовать-es6-функции-стрелок-и-когда-не следует-3d851d7f0b26/