Стрелочные функции в JavaScript хороши не потому, что они короче. Их реальная ценность в другом: они убирают лишний синтаксис у коротких callback-функций и сохраняют внешний this, когда это действительно нужно.

Проблема начинается, когда их начинают использовать везде подряд. Тогда короткий синтаксис превращается в источник путаницы: теряется привычное поведение this, нельзя использовать стрелку как конструктор, а методы объектов начинают вести себя не так, как ожидается.

Ниже разберем, где стрелочные функции делают код чище, а где обычная function по-прежнему остается правильным выбором.

Базовый синтаксис

Самый частый сценарий для стрелочной функции - короткий callback. Возьмем обычный map:

const names = ["Alex", "Julia"];

const newNames = names.map(function (name) {
  return `${name} is cool`;
});

console.log(newNames); // ["Alex is cool", "Julia is cool"]

Стрелочная версия делает то же самое, но без слова function:

const names = ["Alex", "Julia"];

const newNames = names.map((name) => {
  return `${name} is cool`;
});

console.log(newNames); // ["Alex is cool", "Julia is cool"]

Если параметр один, скобки можно убрать:

const names = ["Alex", "Julia"];

const newNames = names.map((name) => `${name} is cool`);
const otherNames = names.map(name => `${name} is cool`);

console.log(newNames); // ["Alex is cool", "Julia is cool"]
console.log(otherNames); // ["Alex is cool", "Julia is cool"]

Когда параметров нет, пустые скобки обязательны:

const values = [1, 2, 3];

const alwaysSame = values.map(() => "fixed");

console.log(alwaysSame); // ["fixed", "fixed", "fixed"]

Когда параметров несколько, скобки тоже обязательны:

const pairs = [
  [2, 3],
  [4, 5],
];

const sums = pairs.map(([a, b]) => a + b);

console.log(sums); // [5, 9]

Неявный возврат и возврат объекта

Если у функции одно выражение, можно не писать return. Это и есть неявный возврат:

const prices = [10, 20, 30];

const withVat = prices.map((price) => price * 1.2);

console.log(withVat); // [12, 24, 36]

Но есть важный нюанс с объектами. Такой код не вернет объект:

const createUser = (name) => {
  name: name;
};

В этом случае JavaScript считает фигурные скобки телом функции, а не объектом. Чтобы вернуть объект неявно, его нужно обернуть в круглые скобки:

const createUser = (name, role) => ({ name, role });

console.log(createUser("Alex", "author"));
// { name: "Alex", role: "author" }

Почему у стрелочных функций особый this

У стрелочной функции нет собственного this. Она берет this из внешней области видимости.

Именно поэтому они так удобны внутри методов, когда нужен callback:

const user = {
  name: "Alex",
  delayedGreeting() {
    setTimeout(() => {
      console.log(`Hello, ${this.name}`);
    }, 100);
  },
};

user.delayedGreeting(); // Hello, Alex

Если заменить стрелочную функцию внутри setTimeout на обычную, this будет зависеть от способа вызова и почти наверняка перестанет указывать на объект user.

То же касается arguments: у стрелочной функции нет собственного объекта arguments, поэтому она берет его из внешней обычной функции.

function logFirstArgument() {
  const readArguments = () => arguments[0];
  return readArguments();
}

console.log(logFirstArgument("hello")); // "hello"

Это полезно, но не стоит строить на этом API. Сегодня почти всегда лучше явно описывать параметры функции.

Где стрелочные функции не подходят

Стрелочная функция не заменяет обычную function. Есть несколько мест, где обычная функция остается правильным выбором.

Методы объекта

Если метод должен работать со своим объектом через this, объявляй его обычным методом:

const counter = {
  value: 0,
  increment() {
    this.value += 1;
    return this.value;
  },
};

console.log(counter.increment()); // 1

Вот так делать не стоит:

const counter = {
  value: 0,
  increment: () => {
    return this.value + 1;
  },
};

Здесь this не будет ссылаться на counter, потому что стрелочная функция не создает собственный контекст вызова.

Конструкторы

Стрелочную функцию нельзя вызвать через new:

const User = (name) => {
  this.name = name;
};

// TypeError: User is not a constructor
new User("Alex");

Если нужен конструктор, используй class или обычную функцию.

Методы прототипа

Для методов прототипа тоже нужен собственный this:

function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = function () {
  return `Hi, ${this.name}`;
};

const alex = new Person("Alex");

console.log(alex.sayHi()); // "Hi, Alex"

Что с именами функций

Старое правило “стрелочные функции всегда анонимные” сегодня формулируется неточно. Синтаксически у стрелки действительно нет имени в объявлении, но движок умеет выводить имя из контекста присваивания:

const sayHi = () => "Hi";

console.log(sayHi.name); // "sayHi"

Поэтому в stack trace и DevTools такие функции обычно видны с понятным именем. Но если тебе важна форма объявления, hoisting или явный конструкторский сценарий, обычная функция все равно подходит лучше.

Итог

Стрелочные функции стоит использовать там, где они реально упрощают код: в коротких callback-функциях, преобразованиях массивов и вложенных обработчиках, которым нужен внешний this.

Не используй их автоматически для методов объектов, конструкторов и всего, где поведение this должно зависеть от способа вызова. В этих случаях короткий синтаксис только скрывает ошибку.