Javascript лямбда стрелочные функции

Опубликовано: 11.05.2024

Существует ещё более простой и краткий синтаксис для создания функций, который часто лучше, чем синтаксис Function Expression.

Он называется «функции-стрелки» или «стрелочные функции» (arrow functions), т.к. выглядит следующим образом:

…Такой код создаёт функцию func с аргументами arg1..argN и вычисляет expression с правой стороны с их использованием, возвращая результат.

Другими словами, это более короткий вариант такой записи:

Давайте взглянем на конкретный пример:

То есть, (a, b) => a + b задаёт функцию с двумя аргументами a и b , которая при запуске вычисляет выражение справа a + b и возвращает его результат.

Если у нас только один аргумент, то круглые скобки вокруг параметров можно опустить, сделав запись ещё короче:

Если нет аргументов, указываются пустые круглые скобки:

Функции-стрелки могут быть использованы так же, как и Function Expression.

Например, для динамического создания функции:

Поначалу функции-стрелки могут показаться необычными и трудночитаемыми, но это быстро пройдёт, как только глаза привыкнут к этим конструкциям.

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

Многострочные стрелочные функции

В примерах выше аргументы использовались слева от => , а справа вычислялось выражение с их значениями.

Порой нам нужно что-то посложнее, например, выполнить несколько инструкций. Это также возможно, нужно лишь заключить инструкции в фигурные скобки. И использовать return внутри них, как в обычной функции.

Здесь мы рассмотрели функции-стрелки как способ писать меньше букв. Но это далеко не всё!

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

А пока мы можем использовать их для простых однострочных действий и колбэков.

Итого

Функции-стрелки очень удобны для однострочных действий. Они бывают двух типов:

Недав­но мы дела­ли про­ект с коле­сом фор­ту­ны. И был там стран­ный спо­соб опре­де­лять функ­ции — стре­лоч­ный. Это спо­соб сэко­но­мить стро­ки кода и сде­лать код чита­е­мым, но так­же с его помо­щью мож­но пре­вра­тить код в кашу. Раз­бе­рём, как устро­е­ны стре­лоч­ные функ­ции в JavaScript.

О чём речь

В про­ек­те с коле­сом у нас был такой фраг­мент кода:

В ком­мен­та­рии напи­са­но, что мы объ­яв­ля­ем функ­цию, но в коде нет стан­дарт­ной фор­мы объ­яв­ле­ния function()<> . Вме­сто это­го идёт кон­стан­та, потом опе­ра­тор при­сва­и­ва­ния, скоб­ки, стрел­ка и какой-то код. Так вот, это и есть при­мер объ­яв­ле­ния стре­лоч­ной функ­ции. Сей­час объ­яс­ним подробнее.

Как объявить стрелочную функцию

Что­бы объ­явить стре­лоч­ную функ­цию, дела­ют так:

let ИмяФункции = (Аргумент1, Аргумент2…) => ДействияФункции

Вме­сто let мож­но исполь­зо­вать const — резуль­тат будет такой же. Полу­ча­ет­ся, что мы как бы объ­яв­ля­ем пере­мен­ную, кото­рая что-то уме­ет делать. Что­бы исполь­зо­вать такую пере­мен­ную, нуж­но напи­сать её имя и в скоб­ках пере­дать аргу­мен­ты — точ­но так же, как и при исполь­зо­ва­нии обыч­ной функции.

Вот самый про­стой при­мер исполь­зо­ва­ния стре­лоч­ной функции:

let sum = (a, b) => a + b;

Здесь мы объ­яви­ли функ­цию sum(), кото­рой пере­да­ют­ся два аргу­мен­та. Внут­ри функ­ции они скла­ды­ва­ют­ся, а резуль­тат сло­же­ния воз­вра­ща­ет­ся при вызо­ве функ­ции. По этой при­чине вызов sum(2,3) вер­нёт чис­ло 5.

Если нуж­но внут­ри функ­ции выпол­нить более одной коман­ды, то исполь­зу­ют фигур­ные скобки:

Чем стрелочные функции отличаются от обычных

На самом деле отли­чий почти нет — это такие же функ­ции, как и обыч­ные. Напри­мер, let sum = (a, b) => a + b; — это то же самое, как если бы мы сде­ла­ли так:

let sum = function(a, b) <

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

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

Динамическое создание функций

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

Допу­стим, если мы пишем веж­ли­во­го чат-бота, то дела­ем такую логику:

  • спра­ши­ва­ем, сколь­ко чело­ве­ку лет;
  • если ему мень­ше 18, то гово­рим ему «При­вет»;
  • ина­че гово­рим «Здрав­ствуй­те».

Запи­шем это в виде стре­лоч­ной функции:

Зачем они нужны, если есть обычные?

Если вы толь­ко начи­на­е­те осва­и­вать про­грам­ми­ро­ва­ние, то раз­ни­цы меж­ду ними нет. В этом слу­чае исполь­зуй­те обыч­ные функ­ции — в них мень­ше шан­сов сде­лать ошибку.

Но если вы уже про­ка­ча­лись и изу­ча­е­те или про­бу­е­те писать слож­ные про­ек­ты, где нуж­но обра­щать­ся к мето­ду this внут­ри функ­ции, исполь­зуя свой­ства внеш­не­го объ­ек­та, — то да, луч­ше исполь­зо­вать стре­лоч­ные функции.

Вывод про­стой: если вы зада­ё­те себе этот вопрос — вам пока не нуж­ны стре­лоч­ные функ­ции. Но знать о них сто­ит уже сейчас.

Стрелочные функции (arrow functions) появились в 6-й редакции Javascript, более известной как ECMAScript 6. Сегодня их можно встретить практически в любом современном приложении, написанном на любом из современных JS фреймворков.

Я бы выделил 3 главных преимущества использования стрелочных функций:

Давайте разберем эти преимущества более детально.

Предположим у нас есть массив данных:

При выводе результата для каждого элемента исходного массива мы использовали обратные кавычки (backticks), которые позволяют совмещать написание строчных значений и переменных.

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

Cтрелочные Функции JS

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

Наша функция принимает один единственный аргумент – fruit . Следовательно, мы можем избавиться от скобок вокруг него.

Также мы можем использовать так называемый implicit return (подразумеваемое возвращение…). Явное возвращение (explicit return) подразумевает использование слова return при возврате результата. Если ваша функция возвращает какой-то один результат, то написание слова return можно опустить.

В результате получаем:

Что мы изменили?

  • Удалили слово return
  • Записали все выражение в одну строчку
  • Удалили фигурные <> скобки

После удаления слова return и фигурных скобок мы получаем implicit return, и нам не требуется уточнять, что наша функция возвращает fresh $ (это предполагается).

Что eсли стрелочная функция не принимает никаких аргументов?

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

Некоторые разработчики вместо пустых скобок используют переменную _ , что имеет по большей мере эстетическое значение.

Лично я предпочитаю использовать () => вместо (_) => , когда в функцию не требуется передовать аргументы.

Все стрелочные функции - безымянные

Для начала, приведу пример функции с указанием названия:

Одним из преимуществ такой функции является, то, что в случае ошибки, мы получаем название функции, где она произошла.

Стрелочные функции являются анонимными, то есть им нельзя присвоить название.

С другой стороны, стрелочную функцию можно присвоить переменной. В этом примере мы объявляем стрелочную функцию и присваиваем ее переменной, используя function expression:

Если, у вас нет жестких требований к более детальному отображению ошибок (с указанием названия функции), смело используйте стрелочные функции!

Значение this в стрелочных функциях

Внутри обычной функции значение this зависит от того, как была вызвана функция.

Например, при обычном выполнении функции, значение this - глобальный объект.

Если вы вызываем функцию объекта, то значением this является сам объект.

У стрелочных функций отсутствует собственный контекст выполнения, следовательно, значение this в стрелочных функциях всегда наследуется от родительской (внешней) функции.

В этом примере мы получим undefined для каждого числа в массиве numbers:

Значение this , которое мы получаем внутри console.log(this.message, number) определяется обычной функцией. Эта функция не привязана к нашему объекту, поэтому получаем - undefined.

Второй вариант - использовать стрелочную функцию.

Значение this внутри обыкновенной функции - зависит от контекста вызова. Значение this в стрелочной функции всегда принимает значение внешней функции!

Одной из самых заметных новшеств современного JavaScript стало появление стрелочных функций (arrow function), которые иногда называют «толстыми» стрелочными функциями (fat arrow function). При объявлении таких функций используют особую комбинацию символов — => .

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

image


Иногда эти и другие преимущества ведут к тому, что стрелочному синтаксису отдают безусловное предпочтение перед другими способами объявления функций. Например, популярная конфигурации eslint от Airbnb принуждает к тому, чтобы всегда, когда создают анонимную функцию, такая функция была бы стрелочной.

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

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

Особенности стрелочных функций в JavaScript

Стрелочные функции в JavaScript — это нечто вроде лямбда-функций в Python и блоков в Ruby.

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

Поговорим об этом подробнее.

▍Синтаксис стрелочных функций

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


Список аргументов функции находится в круглых скобках, после него следует стрелка, составленная из символов = и > , а дальше идёт тело функции в фигурных скобках.

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

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

Рассмотрим вариант синтаксиса, который используется в том случае, если тело функции представлено единственным выражением. Он позволяет обойтись без фигурных скобок, обрамляющих тело функции, и избавляет от необходимости явного возврата результатов вычисления выражения, так как этот результат будет возвращён автоматически. Например, это может выглядеть так:


Вот ещё один вариант сокращённой записи функции, применяемый в том случае, когда функция имеет лишь один аргумент.


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

▍Возврат объектов и сокращённая запись стрелочных функций

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

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


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

Для того чтобы указать системе на то, что мы имеем в виду именно объектный литерал, нужно заключить его в круглые скобки:

▍Стрелочные функции и включающий их контекст выполнения

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

На практике это означает, что они наследуют сущности this и arguments от родительской функции.

Например, сравним две функции, представленные в следующем коде. Одна и них обычная, вторая — стрелочная.


Тут имеется объект test с двумя методами. Каждый из них представляет собой функцию, которая создаёт и возвращает анонимную функцию. Разница между этими методами заключается лишь в том, что в первом из них используется традиционное функциональное выражение, а во втором — стрелочная функция.

Если поэкспериментировать с этим кодом в консоли, передавая методам объекта одни и те же аргументы, то, хотя методы и выглядят очень похожими, мы получим разные результаты:


У анонимной функции есть собственный контекст, поэтому, когда её вызывают, при обращении к test.name не будет выдано значение свойства name объекта, а при обращении к arguments не будет выведен список аргументов функции, которая использовалась для создания и возврата исследуемой функции.

В случае же со стрелочной функцией оказывается, что её контекст совпадает с контекстом создавшей её функции, что даёт ей доступ и к списку аргументов, переданных этой функцией, и к свойству name объекта, методом которого эта функция является.

Ситуации, в которых стрелочные функции улучшают код

▍Обработка списков значений

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

Например, если имеется массив значений, который нужно преобразовать с использованием метода массивов map , для описания такого преобразования идеально подходит стрелочная функция:


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


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

▍Промисы и цепочки промисов

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

Так, промисы значительно упрощают работу с асинхронным кодом. При этом, даже если вы предпочитаете использовать конструкцию async/await, то без понимания промисов вам не обойтись, так как эта конструкция основана на них.

Однако при использовании промисов нужно объявлять функции, которые вызываются после завершения работы асинхронного кода или завершения асинхронного обращения к некоему API.

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

▍Трансформация объектов

Ещё один распространённый пример использования стрелочных функций заключается в инкапсуляции трансформаций объектов.

Например, в Vue.js существует общепринятый паттерн включения фрагментов хранилища Vuex напрямую в компонент Vue с использованием mapState .

Эта операция включает в себя объявления набора «преобразователей», которые выбирают из исходного полного состояния именно то, что нужно для конкретного компонента.

Такие вот простые преобразования — идеальное место для использования стрелочных функций. Например:

Ситуации, в которых не следует использовать стрелочные функции

▍Методы объектов

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

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

Одно время популярным было применение комбинации свойств классов и стрелочных функций для создания методов с «автоматической привязкой», то есть таких, которые могут быть использованы обработчиками событий, но остаются привязанными к классу. Выглядело это примерно так:


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

Однако у такого подхода масса минусов, которым посвящён этот материал.

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

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

▍Длинные цепочки вызовов

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

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

▍Функции с динамическим контекстом

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

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

Вот некоторые вещи, о которых нужно помнить, рассматривая возможность использования стрелочных функций:

  • Обработчики событий вызываются с this , привязанным к атрибуту события currentTarget .
  • Если вы всё ещё пользуетесь jQuery, учитывайте, что большинство методов jQuery привязывают this к выбранному элементу DOM.
  • Если вы пользуетесь Vue.js, то методы и вычисляемые функции обычно привязывают this к компоненту Vue.

Итоги

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

Выражения стрелочных функций имеют более короткий синтаксис по сравнению с функциональными выражениями и лексически привязаны к значению this (но не привязаны к собственному this, arguments, super, или new.target). Выражение стрелочных функций не позволяют задавать имя, поэтому стрелочные функции анонимны, если их ни к чему не присвоить.

Синтаксис

Базовый синтаксис

Расширенный синтаксис

Подробные примеры синтаксиса можно посмотреть здесь.

Описание

Два фактора повлияли на появление стрелочных функции: более короткий синтаксис и лексика this .

Короткие функции

В некоторых функциональных шаблонах приветствуются более короткие функции. Сравните:

Отсутствие связывания с this

До появления стрелочных функций, каждая новая функция имела своё значение this (новый объект в случае конструктора, undefined в strict режиме вызова функции, контекст объекта при вызове функции как "метода объекта" и т.д.). Это очень раздражало при использовании объектно-ориентированного стиля программирования.

В ECMAScript 3/5, данная проблема решалась присваиванием значения this переменной:

Кроме этого, может быть создана привязанная функция, в которую передаётся требуемое значение this для функции (функция growUp() в примере выше).

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

Строгий режим исполнения

Поскольку значение this определяется лексикой, правила строгого режима (strict mode) относительно this игнорируются:

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

Вызов с помощью call или apply

Так как значение this определяется лексикой, вызов стрелочных функций с помощью методов call() или apply() , даже если передать аргументы в эти методы, не влияет на значение this :

Не имеет собственного объекта arguments

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

В большинстве случаев лучшей заменой объекта arguments в стрелочных функциях являются rest параметры:

Использование стрелочных функций как методов

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

Стрелочные функции не объявляют привязку ("bind") их контекста this . Другой пример включает Object.defineProperty() :

Использование оператора new

Стрелочные функции не могут быть использованы как конструктор и вызовут ошибку при использовании с new :

Использование ключевого слова yield

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

Тело функции

Тело стрелочной функции может иметь краткую (concise body) или блочную (block body) форму.

Блочная форма не возвращает значение, необходимо явно вернуть значение.

Возвращаемые объектные строки (литералы)

Помните о том, что возвращаемые объектные строки используют сокращённый синтаксис: params => будет работать не так, как ожидается.

Это происходит потому что код в скобках (<>) распознаётся как цепочка выражений (т.е. foo трактуется как наименование, а не как ключ в объектной строке).

Не забывайте оборачивать скобками объектные строки.

Разрывы строк

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

Разбор порядка следования

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

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