Стрелочные функции JavaScript
Стрелочные функции JavaScript в деталях: лексический this, отсутствие arguments и super, почему нельзя использовать new, и когда их не применять.
Введение в стрелочные функции JavaScript
Стрелочные функции, появившиеся в ES6 (ECMAScript 2015), стали одной из ключевых возможностей JavaScript и предоставляют более простой способ записи функциональных выражений. Они особенно популярны, поскольку упрощают код и помогают решать распространённые проблемы с ключевым словом this.
На этой странице рассматривается не синтаксис (он описан в разделе Стрелочные функции: основы), а причины такого поведения стрелочных функций. Коротко: стрелочная функция не имеет собственных привязок. У неё нет собственного this, собственного arguments, собственного super и метода [[Construct]]. При обращении к любому из этих значений внутри стрелочной функции JavaScript ищет их в окружающей (лексической) области видимости — точно так же, как и обычную переменную. Почти все особенности и преимущества стрелочных функций вытекают именно из этого одного правила.
Определение стрелочных функций
Стрелочные функции позволяют использовать более короткий синтаксис по сравнению с традиционными функциональными выражениями. Вот базовое сравнение:
Версия со стрелочной функцией не только короче, но и устраняет необходимость в ключевом слове function и фигурных скобках при наличии одного выражения.
Варианты синтаксиса
Стрелочные функции можно записывать в различных формах в зависимости от количества параметров и сложности тела функции:
- Без параметров: используйте пустые скобки:
- Один параметр: скобки необязательны:
- Несколько параметров: скобки обязательны:
- Несколько строк: используйте фигурные скобки и явный
return(если функция возвращает значение):
У стрелочных функций нет собственного this
Самое важное свойство стрелочных функций — отсутствие собственного this. Обычная функция определяет своё this в момент вызова в зависимости от того, как она вызвана (подробнее — в разделе Методы объекта, "this"). Стрелочная функция игнорирует это и берёт this из той области видимости, где она была определена — это называется лексическим this.
Именно это нужно, когда стрелочная функция используется как колбэк. Метод, который теряет свой this внутри обычного колбэка, сохраняет его внутри стрелочной функции:
Замените стрелочную функцию обычной function(member) { ... }, и this внутри forEach станет undefined, что вызовет ошибку Cannot read properties of undefined. До появления стрелочных функций разработчики обходили это с помощью const self = this; или .bind(this) — см. раздел Привязка функции. Стрелочные функции избавляют от необходимости в таких приёмах.
То же лексическое правило решает классическую проблему с таймером, когда колбэк выполняется в отрыве от своего объекта:
Поскольку this зафиксирован лексически, изменить его невозможно. Вызов .call(), .apply() или .bind() на стрелочной функции не влияет на this — привязка игнорируется:
У стрелочных функций нет arguments
Стрелочные функции также не имеют собственного объекта arguments. Обращение к arguments внутри стрелочной функции приводит к поиску в окружающей функции — что удобно для обёрток и декораторов:
Если вам действительно нужны аргументы самой стрелочной функции, используйте остаточные параметры (...args), которые работают везде:
Стрелочные функции нельзя использовать как конструкторы
Поскольку у стрелочной функции нет внутреннего метода [[Construct]] и свойства prototype, её нельзя использовать с оператором new:
По той же причине стрелочные функции не могут обращаться к super для доступа к родительскому классу, поэтому они никогда не используются как конструкторы классов или методы классов, опирающиеся на super.
Когда не следует использовать стрелочные функции
Лексический this отлично подходит для колбэков, но неуместен в ряде типичных случаев. Используйте обычную функцию там, где функции нужен собственный динамический this.
Методы объекта
Когда функция является методом объекта, обычно ожидается, что this будет указывать на этот объект. Стрелочная функция берёт this из внешней области видимости (часто это модуль или глобальная область), поэтому она не увидит собственные свойства объекта:
Используйте обычное краткое описание метода (или функциональное выражение) для методов объекта. Подробнее — в разделе Методы объекта, "this".
Методы прототипа и конструкторы
Поскольку стрелочные функции не могут использоваться как конструкторы и не имеют собственного this, они не могут определять методы прототипа или служить функциями-конструкторами. Методы, добавляемые к прототипу, должны быть обычными функциями, чтобы каждый экземпляр правильно разрешал this.
Обработчики событий DOM (в браузере)
При подключении обработчика через addEventListener браузер вызывает его с this, указывающим на элемент, получивший событие. Стрелочная функция игнорирует это и сохраняет внешний this, поэтому используйте обычную функцию, если вам нужно обращаться к элементу через this (впрочем, всегда можно воспользоваться event.currentTarget).
Продвинутые техники
Возврат литералов объекта
Чтобы вернуть литерал object из стрелочной функции, оберните объект в скобки:
IIFE со стрелочными функциями
Стрелочные функции можно использовать для немедленно вызываемых функциональных выражений (IIFE):
Итоги
Стрелочные функции лучше всего понимать через то, чего в них нет. Они не имеют:
- Собственного
this— он берётся из окружающей области видимости (лексическийthis), и.call/.apply/.bindне могут его изменить. Идеально для колбэков, неуместно для методов объекта. - Собственного
arguments— используйте остаточные параметры (...args), если они нужны. super— поэтому они не могут быть методами класса, вызывающими родителя.[[Construct]]иprototype— поэтому их нельзя использовать сnewили как методы прототипа.
Используйте стрелочные функции для коротких колбэков и любых функций, которые должны сохранять внешний this. Используйте обычные функции для методов объекта, методов прототипа, конструкторов и обработчиков DOM-событий, которым нужен динамический this.
Связанные главы
- Стрелочные функции: основы
- Методы объекта, "this"
- Привязка функции
- Конструктор, оператор "new"
- Остаточные параметры и синтаксис spread