Вы их знаете, любите и наверняка постоянно используете! Представленные в 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, чтобы разобраться, когда стоит использовать стрелочные функции, а когда нет.
Перевод статьи Andrew Koenig-Bautista: 3 Examples of When Not to Use JavaScript Arrow Functions
Комментарии