JavaScript

Стрелочные функции ES6

Вы их знаете, любите и наверняка постоянно используете! Представленные в 2015 году как часть обновления ECMAScript 6 стрелочные функции приобрели свою популярность совершенно заслуженно. Их синтаксис  —  прекрасный синтаксический сахар, устраняющий необходимость в: 

  • ключевом слове return (для однострочных функций);
  • ключевом слове function;
  • фигурных скобках.

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

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

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

Допустим, вы хотите создать метод для привязки к объекту:

const mario = {
    lives: 3,
    oneUp: () => {
        this.lives++;
    }
}

В этом примере при вызове mario.oneUp() мы ожидаем, что значение mario.lives увеличится с 3 до 4. Однако значение lives останется неизменным независимо от того, сколько раз вызван oneUp(). Почему? Вот ответ — this!

Согласно MDN:

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

В нашем случае объемлющим контекстом будет объект window. При вызове oneUp() программа будет запрашивать увеличение значения lives в объекте window. Такого значения не существует, поэтому код не работает. 

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

const mario = {
  lives: 3,
  oneUp: function() {
    this.lives++;
  }
};

Прототип объекта

Фрагмент JavaScript, с которым мы будем работать в этом примере: 

class Robot {
  constructor(name, catchPhrase) {
    this.name = name;
    this.catchPhrase = catchPhrase;
  }
};

Robot.prototype.speak = () => {
  console.log(this === window);
  return this.catchPhrase
};

const ironG = new Robot("Iron Giant", "Be good");

ironG.speak();

Вызов функции в строке 15 будет выглядеть так:

true
undefined

Мы определили функцию прототипа speak() и передали в качестве ключевой фразы для нового объекта Robot, так почему же код выдал undefined?

console.log() показывает почему. Как видим в консоли, если (this === window), то выводится true, предоставляя доказательства того, что мы обсуждали в предыдущем примере с методами объекта. 

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

Robot.prototype.speak = function() {
  console.log(this === ironG); // true
  return this.catchphrase;
};

Динамический контекст

Вот последний пример:

const button = document.querySelector(#darkMode);
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

Но сейчас вы, вероятно, знаете, что код не будет работать и почему. Я дам вам подсказку: причина вновь связана с this.

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

При манипуляциях DOM с помощью обработчиков или слушателей инициируемые события указывают на принадлежность this целевому элементу.

Для стрелочной функции, определённой в глобальном контексте выполнения, this будет указывать на window. Значит, в коде выше this.classList будет преобразован в window.classList, что приведёт к TypeError.

Вот и всё!

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

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Andrew Koenig-Bautista: 3 Examples of When Not to Use JavaScript Arrow Functions

Предыдущая статьяОт продвинутой к эффективной аналитике
Следующая статьяИзбавляемся от рендеринга в Angular: только функциональность и никакого рендера