Arrow Function

Стрелочная функция — компактный синтаксис для создания функций с лексическим this, без собственного arguments, super и new.target.

Зачем нужно

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

Где используется

Callback-функции, методы массивов (map/filter/reduce), промисы (.then/.catch), короткие утилиты, React-компоненты.

Предпосылки

Function Declaration, Function Expression, this

Синтаксис

Полная форма (block body)

const add = (a, b) => {
  return a + b;
};

Сокращённая форма (concise body)

const add = (a, b) => a + b;  // return подразумевается

Один параметр — скобки необязательны

const double = x => x * 2;
const greet = name => `Привет, ${name}!`;

Без параметров — скобки обязательны

const now = () => Date.now();
const random = () => Math.random;

Возврат объекта — обернуть в скобки

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

console.log(createUser('Иван', 25));
// { name: 'Иван', age: 25 }

Лексический this

Главное отличие стрелочных функций — они НЕ создают собственный this:

const team = {
  name: 'Альфа',
  members: ['Иван', 'Мария', 'Петр'],

  // Обычная функция — this теряется в callback
  showMembersBroken {
    this.members.forEach(function(member) {
      // this === undefined (strict mode) или window
      console.log(`${this.name}: ${member}`); // Ошибка!
    });
  },

  // Стрелочная функция — this берётся из showMembers
  showMembers {
    this.members.forEach(member => {
      console.log(`${this.name}: ${member}`); // "Альфа: Иван" и т.д.
    });
  }
};

Вложенные стрелки

class Timer {
  constructor {
    this.seconds = 0;
  }

  start {
    // this из start — экземпляр Timer
    setInterval(() => {
      this.seconds++;  // this === экземпляр Timer
      console.log(this.seconds);
    }, 1000);
  }
}

Нет объекта arguments

// Обычная функция — есть arguments
function regularFn() {
  console.log(arguments); // Arguments [1, 2, 3]
}

// Стрелочная — нет arguments
const arrowFn = () => {
  // console.log(arguments); // ReferenceError
};

// Решение: rest-параметры
const arrowWithRest = (...args) => {
  console.log(args); // [1, 2, 3] — обычный массив
};

arrowWithRest(1, 2, 3);

Нельзя использовать как конструктор

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

// new Person('Иван'); // TypeError: Person is not a constructor

// Решение: обычная функция или класс
function PersonFn(name) {
  this.name = name;
}
const ivan = new PersonFn('Иван'); // OK

Нет prototype

const arrow = () => {};
console.log(arrow.prototype); // undefined

function regular() {}
console.log(regular.prototype); // {constructor: f}

Практические примеры

Методы массивов

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const evens = numbers.filter(n => n % 2 === 0);
const doubled = numbers.map(n => n * 2);
const sum = numbers.reduce((acc, n) => acc + n, 0);

console.log(evens);   // [2, 4, 6, 8, 10]
console.log(doubled); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
console.log(sum);     // 55

Цепочка преобразований

const users = [
  { name: 'Алиса', age: 25 },
  { name: 'Боб', age: 17 },
  { name: 'Чарли', age: 30 },
  { name: 'Дина', age: 15 },
];

const adultNames = users
  .filter(u => u.age >= 18)
  .map(u => u.name)
  .sort((a, b) => a.localeCompare(b));

console.log(adultNames); // ['Алиса', 'Чарли']

Промисы

fetch('/api/users')
  .then(response => response.json())
  .then(users => users.filter(u => u.active))
  .then(activeUsers => console.log(activeUsers))
  .catch(err => console.error('Ошибка:', err));

Короткие утилиты

const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

const add10 = x => x + 10;
const multiply2 = x => x * 2;
const toString = x => `Результат: ${x}`;

const transform = pipe(add10, multiply2, toString);
console.log(transform(5)); // "Результат: 30"

Когда НЕ использовать

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

const obj = {
  value: 42,
  // Плохо — this не привязан к obj
  getValue:  => this.value, // undefined

  // Хорошо — shorthand метод
  getValue { return this.value; } // 42
};

Обработчики событий DOM (когда нужен this элемента)

// this будет window/undefined, а не элемент
button.addEventListener('click', () => {
  console.log(this); // window, не button!
});

// Решение 1: обычная функция
button.addEventListener('click', function {
  console.log(this); // button
});

// Решение 2: использовать event.target
button.addEventListener('click', (event) => {
  console.log(event.target); // button
});

Прототипные методы

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

// Плохо
Person.prototype.greet = () => {
  return `Привет, ${this.name}`; // this — не экземпляр
};

// Хорошо
Person.prototype.greet = function {
  return `Привет, ${this.name}`;
};

Сводка: стрелочная vs обычная

Свойство Обычная Стрелочная
this Свой, зависит от вызова Лексический (от окружения)
arguments Есть Нет
new Можно Нельзя
prototype Есть Нет
super Есть Нет собственного
yield Можно (генератор) Нельзя
Hoisting Declaration — да Нет

Частые ошибки

1. Забытые скобки при возврате объекта

// Неправильно — блок кода, не объект!
const getUser = () => { name: 'Иван', age: 25 };
console.log(getUser); // undefined

// Правильно — обернуть в 
const getUser2 = () => ({ name: 'Иван', age: 25 });

2. Многострочная стрелка без return

// Concise body — return автоматический
const add = (a, b) => a + b; // OK

// Block body — нужен явный return!
const addBroken = (a, b) => {
  a + b; // ничего не возвращает!
};

const addFixed = (a, b) => {
  return a + b;
};

3. this в стрелке верхнего уровня

// На верхнем уровне модуля this === undefined (ESM) или module.exports (CJS)
const getThis = () => this;
console.log(getThis); // undefined в модуле, window в скрипте

Практика

  1. Перепиши callback-функции в map/filter/reduce на стрелочные
  2. Создай объект с методом, использующим forEach со стрелочной функцией
  3. Напиши функцию compose, принимающую массив функций
  4. Попробуй использовать new со стрелочной функцией и увидь ошибку
  5. Сравни поведение this в стрелочной и обычной функции внутри метода объекта

Связанные темы

Ресурсы


🎓 Источник: Функции, стрелочные функции, контексты, замыкания

  • 📅 2018-09-27 · YouTube · ID: pn5myCmpV2U
  • Тезисы:
    • Lambda Expression: expression справа возвращается без return; с блоком {}return нужен явно
    • У лямбды нет своего this, она берёт его из контекста выше
    • Лямбды нельзя привязать через bind к контексту — только обычные function
  • Цитата: «Если у нас функция объявлена при помощи ключевого слова function, у нее может быть объектный контекст. А если это lambda функция, у нее нет объектного контекста.»

⚡ Источник: Regular Function vs Arrow Function · AsForJS

  • 📅 2022-08-14 · YouTube · ID: IKB61INTyrs
  • Тезисы:
    • Стрелочная функция в V8 часто создаётся без CreateFunctionContext — оптимизатор не создаёт лишних структур
    • => constValue — единственная настоящая константа в JS (математически доказуемо)
    • Оптимизатор V8 предпочитает стрелочные функции для вложенных конструкций
    • Стрелочные функции дешевле обычных function expression в нагретом коде
  • Цитата: «Эта конструкция, это единственный на текущий момент способ создания реальных констант в академическом смысле этого слова в джаваскрипте.»

⚡ Источник: Как работает this в javascript · AsForJS

  • 📅 2023-05-07 · YouTube · ID: 4tg4qokVS9o
  • Тезисы:
    • У стрелочной функции нет скрытого параметра this — это её единственное отличие
    • При обращении к this внутри стрелки разрешение идёт по цепочке окружений как для любого идентификатора
    • Важно место определения стрелки, не место её вызова
    • Возврат стрелки из метода фиксирует this метода навсегда — где бы стрелку потом ни вызвали
  • Цитата: «Самое простое отличие — это запомнить себе, что нормальная функция — это любая функция, которая не является arrow function, стрелочной функцией.»
  • Код:
    const theObject = {
      name: 'Murych',
      returnFn { return  => console.log(this.name); }
    };
    const superObject = { run(fn) { fn; } };
    superObject.run(theObject.returnFn); // 'Murych'
    // Стрелка запомнила this момента создания (theObject)