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

Итак, вот учебник по стрелочным функциям.

Начнем с того, что стрелочные функции имеют следующий синтаксис

parameters => expression

Выражение может иметь или не иметь дело с переданными параметрами. Вот варианты приведенного выше синтаксиса.

Лаконичный корпус. Для одного параметра скобки не нужны. Для однострочного выражения не требуются фигурные скобки или возвращаемое ключевое слово.

() => console.log("Hello world in Hindi is नमस्ते दुनिया!");

Нет параметра. Всего одно выражение. И если параметр был необходим, круглые скобки необязательны. Только по одному параметру.

name => console.log(`Greetings ${name}!`);

В своих проектах я предпочитаю иметь краткое тело со скобками для параметров, даже если это всего один параметр.

(name) => console.log(`Greetings ${name}!`);

ESLint помогает мне реализовать вышеизложенное. Читаемый код FTW!

Скобки нужны более чем для одного параметра.

(name, age) => console.log(`Greetings ${name}! Looks like you are ${age>18 ? "an adult." : "still not an adult."}`);

Параметры могут иметь значения по умолчанию.

(name = "earthling", age) => console.log(`Greetings ${name}! Looks like you are ${age > 18 ? "an adult.": "still not an adult."}`);

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

(name = "earthling", age) => {
 const adultOrNot = age > 18 ? "an adult.": "still not an adult.";
 console.log(`Greetings ${name}! Looks like you are ${adultOrNot}`);
}

Поскольку в приведенном выше примере мы просто записываем консоль, возврат не требуется. Однако в следующем очень «сложном» способе нахождения високосного года используется возврат.

(year) => {
  const totallyDivisibleByFour = !Boolean(2016 % 4);
  if(totallyDivisibleByFour){
    return true;
  }else{
    return false;
  }
}

Бывают случаи, когда вы хотите вернуть больше логических значений, вы хотите вернуть объекты только в одном выражении.

() => ({answer: [{life: 42}, {universe: 42}, {everything: 42}]});

А может быть нужно, чтобы он сразу исполнялся, IIFE?

(() => ({answer: [{life: 42}, {universe: 42}, {everything: 42}]}))();

И, очевидно, это не должно быть все в одной строке

(
  () => (
    { answer: [{ life: 42 }, { universe: 42 }, { everything: 42 }] }
  )
)();

К настоящему моменту должно быть ясно, что стрелочные функции являются анонимными функциями.

У стрелочной функции нет имени.

Однако вы можете назвать их. Все приведенные выше утверждения можно преобразовать в следующий шаблон.

const nameForYourArrowFunction = parameters => expression;

Что значит

const greet = () => console.log("Hello world in Hindi is नमस्ते दुनिया!");
greet();

И вы можете передавать стрелочные функции другим функциям в качестве аргументов или обратных вызовов.

Помимо синтаксиса, стрелочные функции также отличаются от обычных функций. Давайте сначала займемся более простыми вещами.

В стрелочных функциях нет ключевого слова arguments, поэтому следующее не будет работать.

((a, b, c) => console.log(arguments))();

Uncaught ReferenceError: аргументы не определены

Однако анонимные функции имеют аргументы.

(function(a, b, c){ console.log(arguments)})();

Аргументы [вызываемый: ƒ, Symbol(Symbol.iterator): ƒ]

В случае стрелочных функций вы можете полагаться на остальные параметры, и следующий шаблон должен помочь

((…arguments) => console.log(arguments))();

Стрелочные функции нельзя использовать в качестве конструкторов. Следующее вызовет ошибку

const g = new greet();

Uncaught TypeError: приветствие не является конструктором

Ключевое слово new.target недоступно. Поскольку стрелочные функции нельзя использовать в качестве конструкторов, это также имеет смысл. И никакой собственности prototype тоже. Вы также не можете использовать yield внутри тела стрелочной функции.

И самое главное, стрелочные функции не имеют собственного ключевого слова this или super. Они берут это от своих родителей. И в случае, если родителем является функция стрелки, он будет искать ближайшую родительскую функцию, не являющуюся стрелкой. Вот пример

document.querySelectorAll("a").forEach((anchor) => {
  console.log(`This here points to ${this}`);
  anchor.addEventListener("click", (event) => {
    event.preventDefault();
    console.log(`This here points to ${this}`);
    if (confirm(`Do you really want to visit ${anchor.innerText}?`))
    {
      window.location = anchor.href;
    }
  });
});

Вышеприведенный код использует API DOM. Вы можете просто скопировать вставку и запустить эту консоль JavaScript из любого окна или вкладки в браузере. И верхней ссылкой на это будет глобальный объект окна. И следующая ссылка тоже будет глобальным объектом window. Давайте перепишем его, используя анонимные функции

document.querySelectorAll("a").forEach(function (anchor) {
  console.log(`This here points to ${this}`);
  anchor.addEventListener("click", function (event) {
    event.preventDefault();
    console.log(`This here points to ${this}`);
    if (confirm(`Do you really want to visit ${anchor.innerText}`)) {
      window.location = anchor.href;
    }
  });
});

Теперь первое this будет указывать на глобальный объект window. Однако второй this будет указывать на объект anchor. Перемещение этого на шаг дальше

document.addEventListener("DOMContentLoaded", (event) => {
  console.log(`This here points to ${this}`);
  document.querySelectorAll("a").forEach((anchor) => {
    console.log(`This here points to ${this}`);
    anchor.addEventListener("click", (event) => {
      event.preventDefault();
      console.log(`This here points to ${this}`);
      if (confirm(`Do you really want to visit ${anchor.innerText}?`)) {
        window.location = anchor.href;
      }
    });
  });
});

Все ссылки на это будут указывать на глобальный window. В любой точке нет родительской функции, отличной от стрелки. Введем небольшое изменение

document.addEventListener("DOMContentLoaded", function (event) {
  console.log(`This here points to ${this}`);
  document.querySelectorAll("a").forEach((anchor) => {
    console.log(`This here points to ${this}`);
    anchor.addEventListener("click", (event) => {
      event.preventDefault();
      console.log(`This here points to ${this}`);
      if (confirm(`Do you really want to visit ${anchor.innerText}?`)) {
        window.location = anchor.href;
      }
    });
  });
});

Теперь «this» внутри обработчика кликов указывает на HTMLDocument, а не на объект window.

Все это означает, что стрелочные функции могут не работать должным образом в следующих ситуациях:

  • Создание методов объекта
  • В зависимости от области применения call, apply или bind
  • Использование prototype
  • Логике требуется доступ к объекту arguments
  • Обработчики событий с динамическим контекстом.

Есть способы преодолеть большинство из вышеперечисленных. Например, для обработчиков событий, использующих стрелочные функции, вместо обращения к этому объекту передайте событие в качестве аргумента. Фактически, всегда передавайте события своим обработчикам событий. Вы получаете лучший контроль.

В заключение отметим, что то, как стрелочные функции справляются с этим, упростило написание JavaScript. Движки JavaScript браузера знают, к чему это приведет, и могут выполнить оптимизацию. Как и большинство вещей в разработке программного обеспечения, стрелочные функции не являются панацеей. Используйте их с умом, и вы сможете сделать свой код не только читабельным и менее подверженным ошибкам, но и оптимизированным.