Введение в стрелочные функции (arrow functions) в JavaScript ES6
“Толстые” стрелочные функции (=>), так же известные, как arrow функции – абсолютно новая функциональность в ECMAScript 2015 (ранее известном под именем ES6). Если верить слухам, то в ECMAScript 2015 => синтаксис стал использоваться вместо –> синтаксиса под влиянием CoffeeScript. Так же, не последнюю роль сыграла похожесть передачи контекста this.
У стрелочных функций есть две главные задачи: обеспечить более лаконичный синтаксис; обеспечить передачу лексического this с родительским scope. Давайте детально рассмотрим каждую из них!
Новый синтаксис функций
Классический синтаксис функций в JavaScript отличается ригидностью, будь это функция с одной переменной или страница с множеством функций. При каждом объявлении функци, вам необходимо писать function () <>. Потребность в более лаконичном синтаксисе функций была одной из причин, почему в свое время CoffeeScript стал очень популярен. Эта потребность особенно очевидна в случае с небольшими callback функциями. Давайте просто взглянем на цепочку Promise:
function getVerifiedToken(selector) < return getUsers(selector) .then(function (users) < return users[0]; >) .then(verifyUser) .then(function (user, verifiedToken) < return verifiedToken; >) .catch(function (err) < log(err.stack); >); >
Вверху вы видите более или менее удобоваримый код, написанный с использованием классического синтаксиса function в JavaScript. А вот так выглядит тот же самый код, переписанный с использованием стрелочного синтаксиса:
function getVerifiedToken(selector) < return getUsers(selector) .then(users =>users[0]) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack)); >
- Мы потеряли function и <>, потому что наши callback функции записываются в одну строку.
- Мы убрали (). Теперь они не обертывают список аргументов, когда присутствует только один аргумент (остальные аргументы проходят как исключения; например, (. args) => . ).
- Мы избавились от ключевого слова return. Убирая <>, мы позволяем однострочным стрелочным функциям провести неявный возврат (в других языках такие функции часто называют лямбда функциями).
const getVerifiedToken = selector => < return getUsers() .then(users =>users[0]) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack)); >
Здесь начинается самое интересное. Так как у нашей функции есть только один оператор, мы можем убрать <>, и код будет очень похож на синтаксис CoffeeScript:
const getVerifiedToken = selector => getUsers() .then(users => users[0]) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack));
И все же код выше написан с использованием синтаксиса ES2015. (Я тоже удивился, что он прекрасно скомпилировался.) Когда мы говорим о стрелочных функциях с одним оператором, это не значит, что оператор не может занимать больше одной строки, для удобства использования.
Есть, однако, один существенный минус: убрав <> из стрелочных функций, как мы можем возвратить пустой объект? Например, тот же <>?
const emptyObject = () => <>; emptyObject(); // ?
function () < return 1; >() => < return 1; >() => 1 function (a) < return a * 2; >(a) => < return a * 2; >(a) => a * 2 a => a * 2 function (a, b) < return a * b; >(a, b) => < return a * b; >(a, b) => a * b function () < return arguments[0]; >(. args) => args[0] () => <> // undefined () => (<>) // <>
Лексический this
История о том, как this пытались протащить в JavaScript, уже покрылась пылью. Каждая function в JavaScript задает свой собственный контекст для this. Этот контекст, с одной стороны, очень легко обойти, а, с другой стороны, он крайне раздражает. На примере ниже вы видите код для часов, которые обновляют данные каждую секунду, обращаясь к jQuery:
$('.current-time').each(function () < setInterval(function () < $(this).text(Date.now()); >, 1000); >);
При попытке сослаться на this DOM элемента, заданный через each в callback’е setInterval, мы, к сожалению, получаем совсем другой this, – тот, который принадлежит callback. Обойти этот момент можно, задав переменную that или self:
$('.current-time').each(function () < var self = this; setInterval(function () < $(self).text(Date.now()); >, 1000); >);
$('.current-time').each(function () < setInterval(() =>$(this).text(Date.now()), 1000); >);
Как насчет аргументов?
Одним из минусов стрелочных функций является то, что у них нет собственной переменной arguments, как у обычных функций:
function log(msg) < const print = () =>console.log(arguments[0]); print(`LOG: $`); > log('hello'); // hello
Повторимся, что у стрелочных функций нет this и нет arguments. Однако, приняв это во внимание, вы все же можете получить аргументы, переданные в стрелочные функции с помощью rest-параметров (так же известны, как spread операторы):
function log(msg) < const print = (. args) =>console.log(args[0]); print(`LOG: $`); > log('hello'); // LOG: hello
Как насчет генераторов?
“Толстые” стрелочные функции не могут использоваться как генераторы. Никаких исключений и обходных путей нет. Точка.
Вывод
- Функции с одиночными операторами, которые сразу же делают возврат;
- функции, которые должны работать с this с родительским scope.
ES6 сегодня
Так можно ли воспользоваться возможностями ES6 уже сегодня? Использование транспайлеров стало нормой в последние несколько лет. Ни простые разработчики, ни крупные компании не стесняются их применять. Babel — транспайлер из ES6 в ES5, который поддерживает все нововведения ES6.
Если используете Browserify, то добавить Babel вы сможете всего за пару минут. Конечно же, есть поддержка практически для любого билда с Node.js. Например: Gulp, Grunt и многие другие.
Как насчет браузеров?
Большинство браузеров постепенно добавляют новые функции, но полной поддержки пока нет ни у кого. Что, теперь ждать? Зависит. Имеет смысл начать использовать функции языка, которые станут универсальными через год-два. Это позволит вам комфортно перейти на новый этап. Однако, если вам нужен 100% контроль над исходным кодом, то пока лучше подождать и использовать ES5.
Перевод подготовили: greebn9k(Сергей Грибняк), silmarilion(Андрей Хахарев)
Стрелочные функции, основы
Существует ещё один очень простой и лаконичный синтаксис для создания функций, который часто лучше, чем 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 , чтобы вернуть значение.