Стрелочные функции в JavaScript
После выпуска шестого стандарта ES6 в 2015 году программисты получили ряд новых возможностей и функций в JavaScript. Стрелочные функции, способы объявления переменных let и const , промисы и множество других фундаментальных изменений были доработаны и добавлены в язык с выходом нового стандарта. Из всего списка изменений мы заострим внимание на стрелочных функциях.
Вкратце, стрелочные функции в JavaScript — это новый метод объявления функциональных выражений, который гораздо компактнее и удобнее своего предшественника. Если вы знакомы с лямбда-функциями в Python, то обязательно найдете между ними сходства.
В данной статье мы рассмотрим стрелочные функции и их синтаксис, приведем примеры их использования, а также изучим все тонкости их применения в коде.
Функции
Прежде чем перейти к изучению стрелочных функций, стоит вспомнить традиционный способ объявления функций и функциональных выражений. Эта глава поможет пользователям вспомнить, что такое функции и выражения функций, а также наглядно увидеть главную разницу в их объявлении со стрелочной функцией в следующих разделах статьи.
Функция — это совокупность команд, написанных с целью выполнения конкретной задачи при вызове. Вызывать ее можно сколько угодно раз из любой части кода.
Синтаксис объявления стандартной функции (function declaration statement) выглядит следующим образом:
function имя_функции(набор_параметров) набор_команд;
>
Объявление функции всегда считывается в первую очередь. Это означает, что выполнять ее вызов возможно до объявления. Данный процесс именуется подъемом.
console.log(multiplication(2,5));
function multiplication (x, y) return x * y;
>
Результат работы программы продемонстрирован на картинке ниже.
Функциональное выражение (function definition expression) — это еще один метод объявления функции, в котором она присваивается переменной или константе. Впоследствии ее можно передать аргументом другой функции или использовать в качестве возвращаемого значения.
Синтаксис объявления функционального выражения представлен ниже:
var имя_переменной = function имя_функции (набор_параметров) набор_команд;
>
Параметр имя_функции необязателен. При его отсутствии функция будет считаться анонимной.
В отличие от обычной функции, функциональное выражение можно вызывать только после его объявления в коде.
Пример функционального выражения:
const multiplication = function (x, y) return x * y;
>
console.log(multiplication(2,5));
Результат работы написанного выше кода показан на картинке ниже.
Основы стрелочных функций
Теперь, когда мы вспомнили, что такое функции и функциональные выражения, можно переходить к изучению стрелочных функций.
Стрелочные функции (arrow function expression) — это те же функциональные выражения, но анонимные и с некоторыми особенностями синтаксиса. Ниже представим список их главных особенностей:
Синтаксис стрелочных функций в JavaScript интуитивно понятный и краткий. Его базовая версия представлена ниже:
(набор_параметров) => набор_команд;
>
Как мы видим, главное отличие стрелочных функций от обычных в JS в отсутствии ключевого слова function и добавлении символов => после списка аргументов в скобках.
const multiplication = (x, y) => x * y;
console.log(multiplication(2,5));
В данном примере мы не использовали фигурные скобки, так как тело функции содержит всего одну операцию. Также мы не указывали явный возврат результата, так как это произойдет автоматически.
Результат выполнения кода представлен на картинке ниже.
Работают стрелочные функции только в той области видимости, в который были объявлены, а также не имеют своего функционального контекста выполнения. Это подразумевает, что такие сущности, как this или argument , наследуются исключительно у родительских функций.
Для более детального понимания, ниже приведем пример, где будем использовать и обычную функцию, и стрелочную:
function Animal() this.group = 'Млекопитающие',
this.name = 'Слон',
this.Animal1 = function () console.log('Работа обычной функции:')
console.log(this.group);
function exampleFunction1() console.log(this.name);
>
exampleFunction1();
>
this.Animal2 = function () console.log('Работа стрелочной функции:')
console.log(this.group);
let exampleFunction2 = () => console.log(this.name);
exampleFunction2();
>
>
let x = new Animal();
x.Animal1();
let y = new Animal();
y.Animal2();
В данном примере this.group доступна как внутри this.Animal1 , так и внутри this.Animal2 , так как это два метода одного объекта. Однако внутри этих методов this.name в первом случае возвращает неопределенное значение и мы видим пустоту, так как у данной функции есть собственный контекст (в данном случае — window ). Во втором же случае он ссылается на родительскую область видимости и возвращает this.name = ‘Слон’ . Так мы видим наглядную работу this в обычных и стрелочных функциях.
Результат работы программы представлен на картинке ниже.
Стрелочные функции не могут использоваться в качестве конструкторов, а также не могут иметь собственных свойств и методов, так как у них отсутствует свойство prototype , которое существует у обычных функций. При их вызове с оператором new система выдаст подобную ошибку.
Также стоит сказать о первом предостережении. Так как после стрелки идут фигурные скобки, обозначающие блок кода с командами, пользователь не сможет создать объект внутри функции или вернуть из нее объектный литерал привычным ему способом. Например, следующий фрагмент кода выдаст ошибку:
Система думает, что перед ней представлено тело функции, поэтому в данном фрагменте кода и возникает ошибка. Чтобы это исправить, объектный литерал в примере выше должен быть заключен в круглые скобки:
Примеры использования
Использование стрелочных функций допустимо в любом месте программы, но особенно удобно в функциях обратного вызова, которые принимают другие функции в качестве параметров. В следующем примере мы используем ее в качестве коллбека для метода map :
const example_numbers = [1, 2, 3, 4, 5];
const doubling = example_numbers.map(number => number * 2);
console.log(doubling);
Результат работы кода, который удваивает элементы массива, представлен на картинке ниже.
Так же, как с фигурными скобками, мы можем опустить и круглые скобки, если указан всего один параметр, как в примере выше.
Еще один пример использования стрелочных функций — это использование их в методе reduce . В следующем примере мы используем стрелочную функцию, чтобы сложить все элементы массива:
const example_numbers = [1, 2, 3, 4, 5];
const sum = example_numbers.reduce((total, number) => total + number, 0);
console.log(sum);
В результате мы получим следующее:
Когда следует отказаться от использования стрелочных функций
В данном разделе мы рассмотрим те случаи, в которых лучше отдать предпочтение обычным функциям, чем стрелочным. Зачастую они все связаны с поведением ключевого слова this в коде.
Из-за особенности в работе this , о которой мы говорили в позапрошлой главе, использование стрелочных функций в методах объекта нецелесообразно.
let animal = group: 'Земноводные',
name: 'Озёрная лягушка',
infoAnimal: () => <
console.log('Группа животных -',this.group);
console.log('Название животного -',this.name);
>,
>
animal.infoAnimal();
В примере у нас есть метод infoAnimal , который отвечает за вывод информации об объекте, когда мы его вызываем с помощью animal.infoAnimal() . В данном случае его лексическое окружение — это window . А следовательно она никак не связана с свойствами объекта group и name . Результат работы программы представлен на картинке ниже.
Теперь перепишем код, используя обычную функцию, и посмотрим на результат:
let animal = group: 'Земноводные',
name: 'Озёрная лягушка',
infoAnimal: function () <
console.log('Группа животных -',this.group);
console.log('Название животного -',this.name);
>,
>
animal.infoAnimal();
Как видно по картинке ниже, метод успешно работает.
Помимо методов объектов, рассматриваемые функции также не подойдут для функций с динамическим контекстом, так как они связывают контекст статически. Например, это касается работы с обработчиками или слушателями событий.
Что запомнить
- Стрелочные функции (arrow function expression) — это те же функциональные выражения, но анонимные и с некоторыми особенностями синтаксиса.
- Базовый синтаксис выглядит следующим образом:
(набор_параметров) => набор_команд;
>
- Во время использования рассматриваемых в статье функций допускается выполнять удаление круглых скобок вокруг параметра, если он один. А также допускается использовать неявный возврат, который подразумевает удаление фигурных скобок и ключевого слова return , если в функции реализована всего одна операция.
- Работают стрелочные функции только в той области видимости, в который были объявлены, а также не имеют своего функционального контекста выполнения. Это подразумевает, что такие сущности, как this или argument , наследуются исключительно у родительских функций.
- При работе с методами объекта, динамическим контекстом или конструкторами нужно использовать обычные функции, а не стрелочные.
Стрелочные функции, основы
Существует ещё один очень простой и лаконичный синтаксис для создания функций, который часто лучше, чем Function Expression.
Он называется «функции-стрелки» или «стрелочные функции» (arrow functions), т.к. выглядит следующим образом:
let func = (arg1, arg2, . argN) => expression;
Это создаёт функцию func , которая принимает аргументы arg1..argN , затем вычисляет expression в правой части с их использованием и возвращает результат.
Другими словами, это сокращённая версия:
let func = function(arg1, arg2, . argN) < return expression; >;
Давайте рассмотрим конкретный пример:
let sum = (a, b) => a + b; /* Эта стрелочная функция представляет собой более короткую форму: let sum = function(a, b) < return a + b; >; */ alert( sum(1, 2) ); // 3
Как вы можете видеть, (a, b) => a + b задаёт функцию, которая принимает два аргумента с именами a и b . И при выполнении она вычисляет выражение a + b и возвращает результат.
- Если у нас только один аргумент, то круглые скобки вокруг параметров можно опустить, сделав запись ещё короче:
let double = n => n * 2; // примерно тоже что и: let double = function(n) < return n * 2 >alert( double(3) ); // 6
let sayHi = () => alert("Hello!"); sayHi();
Стрелочные функции можно использовать так же, как и Function Expression.
Например, для динамического создания функции:
let age = prompt("Сколько Вам лет?", 18); let welcome = (age < 18) ? () =>alert('Привет!') : () => alert("Здравствуйте!"); welcome();
Поначалу стрелочные функции могут показаться необычными и даже трудночитаемыми, но это быстро пройдёт по мере того, как глаза привыкнут к этим конструкциям.
Они очень удобны для простых однострочных действий, когда лень писать много слов.
Многострочные стрелочные функции
Стрелочные функции, которые мы видели до этого, были очень простыми. Они брали аргументы слева от => и вычисляли и возвращали выражение справа.
Иногда нам нужна более сложная функция, с несколькими выражениями и инструкциями. Это также возможно, нужно лишь заключить их в фигурные скобки. При этом важное отличие – в том, что в таких скобках для возврата значения нужно использовать return (как в обычных функциях).
let sum = (a, b) => < // фигурная скобка, открывающая тело многострочной функции let result = a + b; return result; // если мы используем фигурные скобки, то нам нужно явно указать "return" >; alert( sum(1, 2) ); // 3
Здесь мы представили главной целью стрелочных функций краткость. Но это ещё не всё!
Стрелочные функции обладают и другими интересными возможностями.
Чтобы изучить их более подробно, нам сначала нужно познакомиться с некоторыми другими аспектами JavaScript, поэтому мы вернёмся к стрелочным функциям позже, в главе Повторяем стрелочные функции.
А пока мы можем использовать их для простых однострочных действий и колбэков.
Итого
Стрелочные функции очень удобны для простых действий, особенно для однострочных.
- Без фигурных скобок: (. args) => expression – правая сторона выражения: функция вычисляет его и возвращает результат. Скобки можно не ставить, если аргумент только один: n => n * 2 .
- С фигурными скобками: (. args) =>< body >– скобки позволяют нам писать несколько инструкций внутри функции, но при этом необходимо явно вызывать return , чтобы вернуть значение.