- Введение в стрелочные функции (arrow functions) в JavaScript ES6
- Когда и почему стоит использовать стрелочные функции ES6, а когда нет
- Синтаксис
- 1. Без параметров
- 1. Один параметр
- 3. Несколько параметров
- 4. Инструкции
- 5. Тело фунцкии — блок
- 6. Литерал объекта
- Стрелочные функции — анонимные
- Главное преимущество: нет своего this
- Когда не следует использовать стрелочные функции
- 1. Методы объекта
- 2. Функции обратного вызова с динамическим контекстом
- 3. Когда ухудшается читаемость кода
- Когда точно стоит использовать стрелочные функции
Введение в стрелочные функции (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(Андрей Хахарев)
Когда и почему стоит использовать стрелочные функции ES6, а когда нет
Привет, Хабр! Представляю вашему вниманию перевод статьи «When (and why) you should use ES6 arrow functions — and when you shouldn’t» автора Cynthia Lee.
Стрелочные функции — наиболее популярная фишка ES6. Это новый, лаконичный способ написания функций.
function timesTwo(params) < return params * 2 >timesTwo(4); // 8
Теперь то же самое при помощи стрелочной функции.
var timesTwo = params => params * 2 timesTwo(4); // 8
Намного короче! Мы можем опустить фигурные скобки и оператор return ( если нет блока, но об этом позже).
Давайте разберемся, чем отличается новый способ от привычного.
Синтаксис
Первое, на что вы быстро обратите внимание, различные вариации синтаксиса. Давайте посмотрим на основные:
1. Без параметров
Если у функции нет параметров, вы можете просто написать пустые круглые скобки перед =>
На самом деле, можно вообще без скобок!
1. Один параметр
Круглые скобки тоже не обязательны
3. Несколько параметров
4. Инструкции
Обычно функциональное выражение возвращает значение, в то время как инструкция отвечает за действие.
Нужно помнить, что в случае со стрелочными функциями, если у нас есть какой-то набор действий/инструкций, нужно обязательно использовать фигурные скобки и оператор return.
Вот пример стрелочной функции, используемой с оператором if:
var feedTheCat = (cat) => < if (cat === 'hungry') < return 'Feed the cat'; >else < return 'Do not feed the cat'; >>
5. Тело фунцкии — блок
Если даже ваша функция просто возвращает значения, но ее тело находится в фигурных скобках, оператор return нужен обязательно.
6. Литерал объекта
Если функция возвращает объектный литерал, его нужно заключить в круглые скобки.
Стрелочные функции — анонимные
Обратите внимание, что стрелочные функции – анонимны, у них нет имени.
Это создает некоторые сложности:
Главное преимущество: нет своего this
В обычных функциях this указывает на контекст, в котором эта функция вызвана. this стрелочной функции такой же как this окружения, в котором объявлена стрелочная функция.
Например, посмотрите на функцию setTimeout ниже:
// ES5 var obj = < id: 42, counter: function counter() < setTimeout(function() < console.log(this.id); >.bind(this), 1000); > >;
В примере выше требуется использовать .bind(this), чтобы передать контекст в функцию. Иначе this будет undefined.
В этом примере не нужно привязывать this. Стрелочная функция возьмет значение this из замыкания.
Когда не следует использовать стрелочные функции
Теперь, думаю, стало понятно, что стрелочные функции не заменяют обычные.
Вот несколько примеров, когда вам вряд ли захочется их использовать.
1. Методы объекта
Когда вы вызываете cat.jumps, количество жизней не уменьшается. Это происходит потому, что this не привязан ни к чему, и наследует значение из замыкания.
2. Функции обратного вызова с динамическим контекстом
Если вам нужен динамический контекст, стрелочная функция — плохой вариант.
var button = document.getElementById('press'); button.addEventListener('click', () => < this.classList.toggle('on'); >);
Если нажать на кнопку, мы получим TypeError. Это связано с тем, что this не привязан к кнопке.
3. Когда ухудшается читаемость кода
С обычными функциями всегда понятно, что вы хотели сказать. Со стрелочными, учитывая разнообразные варианты синтаксиса, некоторые вещи становятся менее очевидными.
Когда точно стоит использовать стрелочные функции
Стрелочные функции отлично подойдут для случаев, когда вам не нужен собственный контекст функции.
Также мне очень нравится использовать стрелочные функции во всяких map и reduce — код так лучше читается.