Стрелочные функции короткие и удобные, поэтому желание использовать их везде — естественная реакция. Но есть одно место, где они не работают как ожидается: определение метода в объект-литерале. Стрелочная не подхватит объект как this, потому что её контекст фиксируется в момент создания функции, а не в момент вызова. И на уровне объекта-литерала этот «момент создания» — окружающий скоуп, а не сам объект.

Что происходит

const meeting = {
  time: '10:00',
  participants: ['Аня', 'Борис', 'Дима'],

  // обычный метод — this = meeting при вызове
  agendaNormal() {
    return `Встреча в ${this.time}, участников: ${this.participants.length}`;
  },

  // стрелочная — this наследуется от окружения (глобальный)
  agendaArrow: () => {
    return `Встреча в ${this.time}, участников: ${this.participants.length}`;
  },
};

meeting.agendaNormal();
// 'Встреча в 10:00, участников: 3'

meeting.agendaArrow();
// TypeError: Cannot read properties of undefined (reading 'time')

Обычный метод agendaNormal при вызове через meeting.agendaNormal() получает this = meeting — стандартное правило метода объекта.

Стрелочная agendaArrow в момент создания находится в глобальной области видимости (объект-литерал не создаёт свою scope), поэтому this внутри неё — глобальный this. В модуле или строгом режиме это undefined, и обращение к this.time падает с ошибкой.

То же в классах

Класс работает по такому же правилу, но с одним важным исключением — class fields:

class Meeting {
  time = '10:00';
  participants = ['Аня', 'Борис'];

  // обычный метод класса — this = экземпляр
  agendaNormal() {
    return `Встреча в ${this.time}`;
  }

  // стрелочная в class field — this = экземпляр (лексически, при создании!)
  agendaArrow = () => {
    return `Встреча в ${this.time}`;
  };
}

const m = new Meeting();
m.agendaNormal();   // 'Встреча в 10:00' — работает
m.agendaArrow();    // 'Встреча в 10:00' — тоже работает

Разница со случаем object-literal: class fields исполняются в момент вызова конструктора, когда this уже указывает на создаваемый экземпляр. Стрелочная функция там «захватывает» экземпляр лексически — и метод получает правильный контекст. Это тот самый паттерн, который в React-классах заменил .bind(this) в конструкторе (см. подглаву 3.2).

Три способа определить метод — и какой когда

В современном JavaScript есть три идиоматичных способа задать метод объекта или класса:

// 1. Обычная функция в объект-литерал (short-method syntax)
const obj1 = {
  method() { return this.field; }
};

// 2. Обычный метод класса
class Cls1 {
  method() { return this.field; }
}

// 3. Class field с arrow-функцией
class Cls2 {
  method = () => { return this.field; };
}

Как выбирать:

  • Object literal с short-method syntax (method() { ... }) — дефолт для простых объектов. Правильный this при вызове через точку.
  • Обычный метод класса — дефолт для классов. Метод хранится на прототипе, общий для всех экземпляров, экономит память. Но this теряется при отделении метода — см. подглаву 3.2.
  • Class field с arrow — когда метод часто передают как callback (event handlers, timers, DOM listeners). this зафиксирован лексически при создании экземпляра, отделение не ломает. Занимает чуть больше памяти — функция создаётся на каждый экземпляр, а не общая на прототипе.

Что не делать никогда: const obj = { method: () => this.field } в объект-литерале. Это выглядит как метод, но им не является — работать не будет.

Итог правила

Стрелочная функция — отличный выбор для callbacks, обработчиков, вложенных функций, короткой обработки массивов через map/filter/reduce. Плохой выбор для методов, которые вызываются через точку от объекта. Единственное исключение — class fields, где «лексический захват» работает нам на руку и даёт стабильный this.