Стрелочные функции были введены шесть лет назад. Большинство современных браузеров теперь поддерживают стрелочные функции. Большинство разработчиков думают, что стрелочные функции — это просто синтаксический сахар для функций. Даже по прошествии шести лет многие фронтенд-разработчики еще не совсем понимают, что такое стрелочные функции и зачем они им.
Итак, вот учебник по стрелочным функциям.
Начнем с того, что стрелочные функции имеют следующий синтаксис
parameters => expression
Выражение может иметь или не иметь дело с переданными параметрами. Вот варианты приведенного выше синтаксиса.
Лаконичный корпус. Для одного параметра скобки не нужны. Для однострочного выражения не требуются фигурные скобки или возвращаемое ключевое слово.
() => console.log("Hello world in Hindi is नमस्ते दुनिया!");
Нет параметра. Всего одно выражение. И если параметр был необходим, круглые скобки необязательны. Только по одному параметру.
name => console.log(`Greetings ${name}!`);
В своих проектах я предпочитаю иметь краткое тело со скобками для параметров, даже если это всего один параметр.
(name) => console.log(`Greetings ${name}!`);
ESLint помогает мне реализовать вышеизложенное. Читаемый код FTW!
Скобки нужны более чем для одного параметра.
(name, age) => console.log(`Greetings ${name}! Looks like you are ${age>18 ? "an adult." : "still not an adult."}`);
Параметры могут иметь значения по умолчанию.
(name = "earthling", age) => console.log(`Greetings ${name}! Looks like you are ${age > 18 ? "an adult.": "still not an adult."}`);
Как можно было заметить, выражение было всего одной строкой, может быть, немного запутанной, но все же одной строкой. И как только ваше выражение становится многострочным, вы переходите к блочной структуре тела.
(name = "earthling", age) => { const adultOrNot = age > 18 ? "an adult.": "still not an adult."; console.log(`Greetings ${name}! Looks like you are ${adultOrNot}`); }
Поскольку в приведенном выше примере мы просто записываем консоль, возврат не требуется. Однако в следующем очень «сложном» способе нахождения високосного года используется возврат.
(year) => { const totallyDivisibleByFour = !Boolean(2016 % 4); if(totallyDivisibleByFour){ return true; }else{ return false; } }
Бывают случаи, когда вы хотите вернуть больше логических значений, вы хотите вернуть объекты только в одном выражении.
() => ({answer: [{life: 42}, {universe: 42}, {everything: 42}]});
А может быть нужно, чтобы он сразу исполнялся, IIFE?
(() => ({answer: [{life: 42}, {universe: 42}, {everything: 42}]}))();
И, очевидно, это не должно быть все в одной строке
( () => ( { answer: [{ life: 42 }, { universe: 42 }, { everything: 42 }] } ) )();
К настоящему моменту должно быть ясно, что стрелочные функции являются анонимными функциями.
У стрелочной функции нет имени.
Однако вы можете назвать их. Все приведенные выше утверждения можно преобразовать в следующий шаблон.
const nameForYourArrowFunction = parameters => expression;
Что значит
const greet = () => console.log("Hello world in Hindi is नमस्ते दुनिया!"); greet();
И вы можете передавать стрелочные функции другим функциям в качестве аргументов или обратных вызовов.
Помимо синтаксиса, стрелочные функции также отличаются от обычных функций. Давайте сначала займемся более простыми вещами.
В стрелочных функциях нет ключевого слова arguments
, поэтому следующее не будет работать.
((a, b, c) => console.log(arguments))();
Uncaught ReferenceError: аргументы не определены
Однако анонимные функции имеют аргументы.
(function(a, b, c){ console.log(arguments)})();
Аргументы [вызываемый: ƒ, Symbol(Symbol.iterator): ƒ]
В случае стрелочных функций вы можете полагаться на остальные параметры, и следующий шаблон должен помочь
((…arguments) => console.log(arguments))();
Стрелочные функции нельзя использовать в качестве конструкторов. Следующее вызовет ошибку
const g = new greet();
Uncaught TypeError: приветствие не является конструктором
Ключевое слово new.target
недоступно. Поскольку стрелочные функции нельзя использовать в качестве конструкторов, это также имеет смысл. И никакой собственности prototype
тоже. Вы также не можете использовать yield
внутри тела стрелочной функции.
И самое главное, стрелочные функции не имеют собственного ключевого слова this
или super
. Они берут это от своих родителей. И в случае, если родителем является функция стрелки, он будет искать ближайшую родительскую функцию, не являющуюся стрелкой. Вот пример
document.querySelectorAll("a").forEach((anchor) => { console.log(`This here points to ${this}`); anchor.addEventListener("click", (event) => { event.preventDefault(); console.log(`This here points to ${this}`); if (confirm(`Do you really want to visit ${anchor.innerText}?`)) { window.location = anchor.href; } }); });
Вышеприведенный код использует API DOM. Вы можете просто скопировать вставку и запустить эту консоль JavaScript из любого окна или вкладки в браузере. И верхней ссылкой на это будет глобальный объект окна. И следующая ссылка тоже будет глобальным объектом window
. Давайте перепишем его, используя анонимные функции
document.querySelectorAll("a").forEach(function (anchor) { console.log(`This here points to ${this}`); anchor.addEventListener("click", function (event) { event.preventDefault(); console.log(`This here points to ${this}`); if (confirm(`Do you really want to visit ${anchor.innerText}`)) { window.location = anchor.href; } }); });
Теперь первое this будет указывать на глобальный объект window
. Однако второй this будет указывать на объект anchor
. Перемещение этого на шаг дальше
document.addEventListener("DOMContentLoaded", (event) => { console.log(`This here points to ${this}`); document.querySelectorAll("a").forEach((anchor) => { console.log(`This here points to ${this}`); anchor.addEventListener("click", (event) => { event.preventDefault(); console.log(`This here points to ${this}`); if (confirm(`Do you really want to visit ${anchor.innerText}?`)) { window.location = anchor.href; } }); }); });
Все ссылки на это будут указывать на глобальный window
. В любой точке нет родительской функции, отличной от стрелки. Введем небольшое изменение
document.addEventListener("DOMContentLoaded", function (event) { console.log(`This here points to ${this}`); document.querySelectorAll("a").forEach((anchor) => { console.log(`This here points to ${this}`); anchor.addEventListener("click", (event) => { event.preventDefault(); console.log(`This here points to ${this}`); if (confirm(`Do you really want to visit ${anchor.innerText}?`)) { window.location = anchor.href; } }); }); });
Теперь «this
» внутри обработчика кликов указывает на HTMLDocument
, а не на объект window
.
Все это означает, что стрелочные функции могут не работать должным образом в следующих ситуациях:
- Создание методов объекта
- В зависимости от области применения
call
,apply
илиbind
- Использование
prototype
- Логике требуется доступ к объекту
arguments
- Обработчики событий с динамическим контекстом.
Есть способы преодолеть большинство из вышеперечисленных. Например, для обработчиков событий, использующих стрелочные функции, вместо обращения к этому объекту передайте событие в качестве аргумента. Фактически, всегда передавайте события своим обработчикам событий. Вы получаете лучший контроль.
В заключение отметим, что то, как стрелочные функции справляются с этим, упростило написание JavaScript. Движки JavaScript браузера знают, к чему это приведет, и могут выполнить оптимизацию. Как и большинство вещей в разработке программного обеспечения, стрелочные функции не являются панацеей. Используйте их с умом, и вы сможете сделать свой код не только читабельным и менее подверженным ошибкам, но и оптимизированным.