Стрілецькі функції. Стрілецькі функції в ES6

  • Переклад

“Товсті” стрілочні функції (=>), також відомі, як arrow функції – абсолютно нова функціональність у ECMAScript 2015 (раніше відомому під ім'ям ES6). Якщо вірити чуткам, то ECMAScript 2015 => синтаксис став використовуватися замість –> синтаксису під впливом CoffeeScript . Також, не останню роль зіграла схожість передачі контексту цього.

У стрілочних функцій є дві основні завдання: забезпечити більш короткий синтаксис; забезпечити передачу лексичного цього з батьківським scope. Давайте детально розглянемо кожну із них!

Новий синтаксис функцій Класичний синтаксис функцій JavaScript відрізняється ригідністю, будь то функція з однією змінною або сторінка з безліччю функцій. При кожному оголошенні функції вам необхідно писати function () (). Потреба більш лаконічному синтаксисі функцій була однією з причин, чому свого часу CoffeeScript став дуже популярним. Ця потреба особливо очевидна у випадку з невеликими callback функціями. Давайте просто поглянемо на ланцюжок Promise:
function getVerifiedToken(selector) ( return getUsers(selector) .then(function (users) ( return users; )) .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) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack)); )
Тут треба звернути увагу на кілька важливих моментів:
  • Ми втратили функцію і (), тому що наші callback функції записуються в один рядок.
  • Ми прибрали (). Тепер вони не обгортають список аргументів, коли є лише один аргумент (інші аргументи проходять як винятки; наприклад, (...args) => ...).
  • Ми позбулися ключового слова return. Забираючи (), ми дозволяємо однорядковим стрілочним функціям провести неявне повернення (іншими мовами такі функції часто називають лямбда функціями).
Ще раз звернемо увагу на останній пункт. Неявне повернення відбувається тільки у випадку з однорядковими стрілочними функціями. Коли стрілочна функція визначається з (), навіть якщо вона є окремим оператором, неявне повернення не відбувається.
const getVerifiedToken = selector => ( return getUsers() .then(users => users) .then(verifyUser) .then((user, verifiedToken) => verifiedToken) .catch(err => log(err.stack)); )
Тут починається найцікавіше. Оскільки наша функція має лише один оператор, ми можемо прибрати (), і код буде дуже схожий на синтаксис CoffeeScript :
const getVerifiedToken = selector => getUsers() .then(users => users) .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; ) (...args) => args () => () // undefined () => (()) // ()

Історія про те, як це намагалися протягнути в JavaScript, вже вкрилася пилом. Кожна функція JavaScript задає свій власний контекст для цього. Цей контекст, з одного боку, дуже легко оминути, а з іншого боку, він вкрай дратує. На прикладі нижче ви бачите код для годинника, який оновлює дані кожну секунду, звертаючись до jQuery:
$(".current-time").each(function () ( setInterval(function () ( $(this).text(Date.now()); ), 1000); ));
При спробі послатися на цей 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); print(`LOG: $(msg)`); ) log("hello"); // hello
Повторимося, що у стрілочних функцій немає цих і немає arguments. Однак, взявши це до уваги, ви все ж таки можете отримати аргументи, передані в стрілочні функції за допомогою rest-параметрів (так само відомі, як spread оператори):
function log(msg) ( const print = (...args) => console.log(args); print(`LOG: $(msg)`); ) log("hello"); // LOG: hello Як щодо генераторів? "Товсті" стрілочні функції не можуть використовуватися як генератори. Жодних винятків та обхідних шляхів немає. Висновок “Товсті” стрілочні функції – одна з причин, чому я так люблю JavaScript. Дуже спокусливо просто почати використовувати => замість function. Я бачив цілі бібліотеки, де використовується лише варіант =>. Не думаю, однак, що це розумно. Зрештою, у => є багато особливостей та прихованих функцій. Я рекомендую використовувати стрілочні функції тільки там, де вам потрібна нова функціональність:
  • Функції з одиночними операторами, які відразу роблять повернення;
  • функції, які повинні працювати з цим з батьківським scope.
ES6 сьогодні Чи можна скористатися можливостями ES6 сьогодні? Використання транспайлерів стало нормою останні кілька років. Ні прості розробники, ні великі компанії не соромляться застосовувати їх.

ES6 має новий спосіб створення функцій - За допомогою оператора Стрілка => . Такі функції називаються стрілочними. Вони пропонують компактніший синтаксис. Вони не мають імені і вони по-своєму працюють з цим.

Перше, що ми зробимо, це запустимо скрипт Babel, який стежитиме за файлами та при їх зміні створювати свіжі версії.

Відкриємо папку проекту у командному рядку (КС). Вводимо команду:

І натиснути Enter

У папці src створимо файл arr.js і одразу вкажу його у файлі index.html

</script>

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

Давайте напишемо функцію, яка складає два числа і повертає їхню суму. Назвемо функцію add.

Function add (x, y) ( return x + y; ) console.log (add (3, 6));

У консолі ми побачимо результат - 9

Тепер, давайте переведемо цю функцію стрілочну.

Приберемо слово function, приберемо ім'я функції та приберемо фігурні дужки, і слово – return. Після параметрів поставимо стрілку.

Let add = (x, y) => x + y; console.log (add (4, 6));

Якщо дивитися на тип змінної add використовуючи оператор typeof:

Console.log(typeof(add));

Що ми побачимо у консолі function

Це означає, що стрілочні функції це звичайні функції. І це можна переконатися подивившись на транспильований код.

"use strict"; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) ( return typeof obj; ) : function (obj) ( return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj ;); var add = function add(x, y) (return x + y;); console.log(add(4, 6)); console.log(typeof add === "undefined" ? "undefined" : _typeof(add));

Ми бачимо, що Babel перетворив наш код на простий вираз функцією.

Давайте напишемо просту функцію яка зводитиме задане число в квадрат.

Let add = (x, y) => x + y; console.log (add (4, 6)); console.log(typeof(add)); let square = function(a) ( return a * a; ) console.log(square (4));

Подивимося в консолі:

Стрілочна функція виглядатиме ось так:

Let square = x => x * x;

Якщо стрілочна функція приймає лише один параметр, немає необхідності укладати його в дужки!

Давайте напишемо функцію, яка взагалі не приймає параметрів.

Function givNumer() ( return 33; ) console.log(givNumer());

Ця функція просто виводить у консоль число 33.

Let givNumer = () => 33; console.log(givNumer());

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

Let log = function () ( console.log("Hello World!"); ); log();

Стрілочна:

Let log = () => console.log("Hello World!!!"); log();

Створимо функцію тіло якої складатиметься з двох рядків.

Функція прийматиме два параметри. Тілі функції створимо змінну. Після цього повернемо результат.

Let mult = function (a, b) ( let result = a * b; return result; ) console.log(mult (4, 5));

Якщо у стрілочній функції кілька рядків, то фігурні дужки – () обов'язкові! І обов'язково визначити те, що ця функція повертає, використовуючи ключове слово return

Стрілочна:

Let mult = (a, b) => ( let result = a * b; return result; ) console.log(mult (4, 5));

Тепер створимо функцію, яка повертає літерал об'єкта:

Let literal = function () ( return ( name: "John"); ) console.log (literal ());

У консолі ми побачимо:

Тепер спробуємо створити стрілочну функцію, яка повертатиме літерал об'єкта.

Слід пам'ятати, що якщо стрілочна функція повертає літерал об'єкта, то потрібні круглі дужки.

Стрілка функція, що повертає літерал об'єкта:

Let literal = () => ((name: "John")); console.log (literal());

Тепер спробуємо використовувати стрілочну функцію як IIFE - Immediately-invoked function expression

Якщо сказати коротко, то це функція, яка виконується одразу після оголошення

Це виглядає так:

(function () ( console.log("IIFE"); ))();

Стрілка IIFE - функція виглядатиме ось так:

(() => console.log("IIFE"))();

Важливою особливістю стрілочних функцій є те, що стрілка має йти одразу після параметрів!

Її не можна просто так взяти і спустити на рядок нижче. Видасть помилку!

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

Давайте створимо масив з деякими числами та назвемо його numbers. Я думаю що ви знаєте, що масиви мають корисні методи, які дозволяють це масив перебирати, фільтрувати і т.д.

Давайте ви вважаємо суму всіх змінних масиву. Для цього я оголошу ще одну змінну – let sum = 0;

Скористаємося методом forEach() який є у кожного масиву, ми переберемо елементи та додамо до суми.

Let numbers =; let sum = 0; numbers.forEach(function(num) ( sum += num; )); console.log (sum);

У консолі ми побачимо 55 . Давайте перетворимо цю функцію на стрілочну: numbers.forEach(num => sum += num); console.log(sum);

Таким чином, те, що у нас раніше займало три рядки, тепер займає один.

Також ми можемо звести у квадрат кожен елемент масиву.

Let squared = numbers.map (n => n * n); console.log (squared);

Стрілецькі функції і це. Для цього я створимо літерал об'єкта, який збережу в змінну рerson.

У об'єкта person буде властивість name зі значенням 'Bob' і властивості greet - привітати.

Let person = ( name: "Bob", greet: function () ( console.log("Hello! My name is " + this.name); console.log(this); ) ); person.greet();

У консолі браузера ми побачимо вітання і сам об'єкт person.

Тепер ми замінимо функцію на стрілочну і подивимося, що станеться з this .

Let person = ( name: "Bob", greet: () => ( console.log("Hello! My name is " + this.name); console.log(this); ) ); person.greet();

Тепер ми не отримали значення імені і як значення this-window!

Але чому? Справа в тому, що значення цього береться з контексту, в якому функція оголошена. ! Незалежно від того, де ця функція буде виконана. Це можна побачити на зображенні:

Ми маємо програму.

У ній поки що, крім об'єкта window, нічого немає. Додали об'єкт особи. Зауважте, що у методу ми використовуємо стрілочну функцію. Як ми й казали - значення цього буде братися з контексту. Контекст – це оточення. У разі оточенням об'єкта person , всіх його властивостей і методів, буде об'єкт window . І якщо значення this буде братися з контексту, this буде посилатися на об'єкт window .

Якщо ми розглянемо звичайну функцію, ми знаємо, що це посилається сам об'єкт person . Ви можете запитати, чому значення цього у стрілочних функціях береться з контексту? А відповідь дуже проста – їх так зробили! :-) Справа в тому, що стрілочні функції були створені для вирішення проблем в іншій ситуації. Давайте подивимося на прикладі. Для того, щоб побачити проблему, ми повернемося до нашої стрілочної функції.

Let person = ( name: "Bob", greet: function () ( console.log("Hello! My name is " + this.name); console.log(this); ) );

Уявимо, що наш Bob досить зайнятий і йому потрібно кілька секунд, щоб завершити свою роботу. Очікування в 2 сек. ми симулюємо з допомогою функції setTimeout(); . Як перший параметр ця функція приймає функцію і другим параметром - кількість мілісекунд, які необхідно почекати.

Let person = ( name: "Bob", greet: function () ( setTimeout(function () ( console.log("Hello! My name is " + this.name); console.log(this); ), 2000)) ;)); person.greet();

Якщо у вас є досвід роботи з JavaScript, то я думаю, що ви розумієте, в чому полягає проблема. Все одно, подивимося на те, що буде в браузері. Рівно через дві секунди ми побачимо в браузері таку картину.

Але чому? Якщо подивитися наш код, то логічно припустити. що це посилається на об'єкт person , тому що ми використовуємо звичайну функцію. Справа в тому, що setTimeout() належить об'єкту window. Якщо написати так: window.setTimeout() , то як ви думаєте, на що посилається thus ? І в консолі ми отримаємо той самий результат! ES5 має кілька способів вирішити цю проблему. Ми розглянемо найпоширеніший: Перед setTimeout() я оголошу ще одну змінну that і як значення надамо this . І тепер, у тілі функції замість this ми вкажемо that .

Let person = ( name: "Bob", greet: function () ( let that = this; setTimeout(function () ( console.log("Hello! My name is " + that.name); console.log(that) ;), 2000); person.greet();

Тепер завдяки замиканню Функція, яку ми відправляємо в setTimeout(), матиме доступ до змінної that , значенням якої буде this , тобто в даному випадку об'єкт person .

Можна наочності подивитися те що, що посилаються наші that і this .

Let person = ( name: "Bob", greet: function () ( let that = this; setTimeout(function () ( console.log("Hello! My name is " + that.name); console.log("It is my That = "+ that); console.log("It is my This = "+ this); ), 2000);))); person.greet();

У консолі ми побачимо підтвердження:

Ми бачимо, що this буде об'єктом вікна - This = , а that буде об'єктом нашого person - That = .

В ES6 для вирішення цієї проблеми ми можемо просто використовувати стрілочну функцію.

Let person = ( name: "Bob", greet: function () ( setTimeout(() => ( console.log("Hello! My name is " + this.name); console.log("It is my This = "+ this); ), 2000); ))); person.greet();

У результаті ми в консолі побачимо:

У графічному прикладі для стрілочної функції контекстом буде об'єкт person, а не об'єкт window. саме тому this буде посилатися на person.

Крім компактного синтаксису, стрілочні функції були введені для вирішення таких проблем.

Як ознайомлення, ви можете подивитися як вирішив це Babel

Var person = ( name: "Bob", greet: function greet() ( var _this = this; setTimeout(function () ( console.log("Hello! My name is " + _this.name); console.log(" It is my This = "+ _this); ), 2000);))); person.greet(); Babel використовував той же метод, що і ми в ES5. Вся різниця в тому, що ми називали змінну that , а Babel назвав - _this . Завдяки замиканню, функція яку ми відправляємо в setTimeout, матиме доступ до змінної _this і як наслідок - до об'єкта person.

Думаю, що найважче в цій частині – це зрозуміти як працюють замикання.

Ще деякі особливості стрілочних функцій:
Ще інформацію щодо ES6 та стрілочних функцій ви можете переглянути в моєму пості

  • Tutorial

Однією з найцікавіших частин нового стандарту ECMAScript 6 є стрілочні функції. Стрілецькі функції, як і зрозуміло з назви, визначаються новим синтаксисом, який використовує стрілку => . Однак, крім відмінного синтаксису, стрілочні функції відрізняються від традиційних функцій та інших моментах:

  • Лексичне зв'язування. Значення спеціальних змінних this , super і arguments визначаються тим, як стрілочні функції були викликані, а тим, як вони були створені.
  • Незмінні this, super і arguments. Значення цих змінних усередині стрілочних функцій залишаються незмінними протягом усього життєвого циклу функції.
  • Стрілецькі функції не можуть бути використані як конструктор і кидають помилку при використанні з оператором new.
  • Недоступність власного значення змінної arguments .
Було кілька причин для запровадження цих відмінностей. Першочергова - це те, що зв'язування (binding) використовується досить часто JavaScript. Дуже легко втратити потрібне значення при використанні традиційних функцій, що може призвести до непередбачуваних наслідків. Інша причина, це те, що JS-движки зможуть легко оптимізувати виконання стрілочних функцій за рахунок цих обмежень (на противагу традиційним функціям, які можуть бути використані як конструктор і які вільні для модифікації спеціальних змінних).


Примітка: Ця стаття - це компіляція з вільного перекладу статті Understanding ECMAScript 6 arrow functions та читання останньої чернетки специфікації (January 20, 2014 Draft Rev 22).

Синтаксис У випадку синтаксис стрілочних функцій виглядає так:

Var fun = (x) => x;
Він дуже схожий на аналогічний синтаксис у таких мовах як Scala, CoffeeScript та синтаксис lambda-виразів з C#.

Синтаксис стрілочних функцій може бути різним, залежно від того, як ви оголошуєте функцію. Оголошення завжди починається зі списку аргументів, далі слідує стрілка та тіло функції. І список аргументів, і тіло функції можуть мати різну форму залежно від того, що ви пишете.

Один параметр Оголошення стрілочної функції, яка приймає один аргумент і просто повертає його, виглядає дуже просто:

Var reflect = value => value; // еквівалент var reflect = function(value) ( ​​return value; )
Коли стрілочна функція має лише один аргумент, то він може бути оголошений без дужок. Наступне після стрілки тіло функції може бути без фігурних дужок і може не містити ключового слова return .

Декілька параметрів Але якщо ви хочете оголосити більше одного параметра, то повинні обрамити список параметрів у круглі дужки:

Var sum = (num1, num2) => num1 + num2; // еквівалент var sum = function(num1, num2) (return num1 + num2;);
Функція sum просто підсумовує два аргументи. Єдина відмінність від попереднього прикладу в наявності круглих дужок та коми (прямо як у традиційних функціях).

Без параметрів Аналогічно, функція без жодних аргументів повинна мати порожній список параметрів, укладений у круглі дужки:

Var sum = () => 1 + 2; // еквівалент var sum = function() ( return 1 + 2; );

Традиційний синтаксис тіла функції Ви можете скористатися синтаксисом традиційних функцій для тіла стрілочної функції, коли воно містить більше одного виразу. Тобто обернути функцію у фігурні дужки та додати ключове слово return :

Var sum = (num1, num2) => (return num1 + num2;) // еквівалент var sum = function(num1, num2) (return num1 + num2;);
Тіло функції буде оброблено точно так, як і у випадку класичних функцій, за винятком того, що значення спеціальних змінних this , super та arguments будуть обчислюватися по-іншому.

Літерал об'єкта Окремо треба згадати, що тіло функції, яке не містить фігурних дужок і просто повертає літерал об'єкта, має бути забрано в круглі дужки:

Var getTempItem = id => ((id: id, name: "Temp")); // еквівалент var getTempItem = function(id) (return (id: id, name: "Temp"));
Поміщення літералу об'єкта в круглі дужки показує парсеру, що фігурні дужки це початок традиційного синтаксису для тіла функції, а початок літерала.

Змінна кількість параметрів Так як «власний» об'єкт arguments не доступний всередині стрілочної функції (значення arguments лексично пов'язане зі значенням arguments традиційної функції, всередині якої стрілкова функція була оголошена), то для стрілочних функцій зі змінним числом параметрів потрібно використовувати rest-патерн з шаблонів деструктуризації. Приклад:

Var getTempItems = (...rest) => rest; // еквівалент var getTempItems = function() ( return .slice.apply(arguments) );

Шаблон деструктуризації як параметр У рамках цієї статті ми не розглядаємо шаблони деструктуризації - ви можете прочитати про них у статті Огляд ECMAScript 6, наступної версії JavaScript, хоча ця інформація частково застаріла.

Як видно з попереднього прикладу, незважаючи на те, що у стрілочної функції лише один аргумент, все одно необхідно застосовувати круглі дужки при використанні шаблонів деструктуризаціїяк єдиний параметр функції. Приклади з іншими шаблонами:

Var a = ((a)) => a; var b = ([b]) => b;

Використання стрілочних функцій Встановлення контексту Одним із частих сценаріїв JavaScript є встановлення правильного значення цього всередині функції (зв'язування). Оскільки значення цього може бути змінено, то, залежно від контексту виконання функції, можливо помилково впливати на один об'єкт, коли ви мали на увазі зовсім інший. Подивіться наступний приклад:

Var pageHandler = ( id: "123456" , init: function() ( document.addEventListener("click", function(event) ( this.doSomething(event.type); // помилка )); ) , doSomething: function( type) ( console.log ( " Handling " + type + " for " + this.id) ) );
У наведеному коді об'єкт pageHandler повинен обробляти кліки на сторінці. Метод init() навішує обробник на потрібну подію, який у собі викликає this.doSomething() . Проте код відпрацює неправильно. Посилання на this.doSomething() не є валідним, оскільки це вказує на об'єкт document всередині обробника події замість планованого pageHandler . При спробі виконати цей код ви отримаєте помилку, оскільки об'єкт document не має методу doSomething .

Ви можете зав'язати це значення на об'єкті pageHandler, використовуючи handleEvent або викликавши у функції стандартний метод bind() :

Var pageHandler = ( id: "123456" , init: function() ( document.addEventListener("click", (function(event) ( this.doSomething(event.type); // error )).bind(this))) ; ) , doSomething: function(type) ( console.log("Handling " + type + " for " + this.id) ) );
Тепер код працює так, як і думалося, але виглядає більш громіздко. Крім того, викликаючи bind(this) ви щоразу створюєте нову функцію, значення this якої зав'язане на значенні pageHandler , але код працює так, як ви задумували.

Стрілецькі функції вирішують проблему більш елегантним способом, оскільки використовують лексичне зв'язування значення this (а також super і arguments ) і його значення визначається значенням this у тому місці, де стрілочна функція була створена. Наприклад:

Var pageHandler = ( id: "123456" , init: function() ( document.addEventListener("click", event => this.doSomething(event.type)); ) , doSomething: function(type) ( console.log( "Handling" + type + "for" + this.id))));
У цьому прикладі обробник це стрілочна функція, в якій викликається this.doSomething() . Значення this буде тим самим, що і функції init() , і код в даному прикладі відпрацює правильно, аналогічно тому, який використовував bind() . Незалежно від того, чи повертає виклик this.doSomething() значення чи ні, вираз усередині тіла стрілочної функції не потрібно обрамляти у фігурні дужки.

Крім того, приклад вищий ще й ефективніший за виклик bind() , тому що для браузера він аналогічний наступному коду:

Var pageHandler = ( id: "123456" , init: function() ( var self = this; document.addEventListener( "click", function(event) ( return self.doSomething(event.type) )); ) , doSomething: function(type) ( console.log("Handling" + type + "for" + this.id) ) );
Тобто не відбувається створення нової функції, як у разі виклику bind() .

"Прокидання" контексту між кількома викликами Очевидно, що можна вкладати одну стрілочну функцію в іншу, тим самим "прокидаючи" значення this через них:

Var obj = ( arr1: , arr2: ["a", "b", "c"] , concatenate: function(a, b)( return a + "|" + b ) , intersection: function() ( return this .arr1.reduce((sum, v1) => // arrow function 1 this.arr2.reduce((sum, v2) => ( // arrow function 2 sum.push(this.concatenate(v1, v2)) return sum;)), sum),)))); var arrSum = obj.intersection();//["1|a", "1|b", "1|c", "2|a", "2|b", "2|c", "3 |a", "3|b", "3|c"]

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

Var result = values.sort(function(a, b) ( return a - b ));
Досить багатослівно для простої операції. Порівняйте з коротким записом стрілочної функції:

Var result = values.sort((a, b) => a - b);
Використання таких методів, як масивні sort() , map() , reduce() тощо, може бути спрощено з використанням короткого синтаксису стрілочної функції.

Інші особливості стрілочних функцій Незважаючи на те, що стрілочні функції відрізняються від традиційних функцій, вони мають спільні риси:
  • Оператор типуповерне "функцію" для стрілочної функції
  • Стрілочна функція також екземпляр «класу» Function, тому instanceof спрацює так само, як і з традиційною функцією
  • Ви все ще можете використовувати методи call() , apply() і bind() , однак пам'ятайте, що вони не будуть впливати на значення this
  • Ви можете використовувати метод toMethod() , однак він не змінюватиме значення super ( метод toMethod() введений в es6 і не розглядається в рамках цієї статті).
Істотною відмінністю від традиційних функцій є те, що спроба викликати стрілочну функцію із зазначенням оператора new викличе помилку виконання.

Короткий синтаксис дозволить писати складні речі ще складніше простіше. Наприклад, так буде виглядати генератор ідентифікаторів (який на es5 виглядає куди багатослівнішим):

Let idGen = (start = 0, id = start, reset = (newId = start) => id = newId, next = () => id++) => ((reset, next)); let gen = idGen(100); console.log(gen.next(), gen.next(), gen.reset(10), gen.next());//100 101 10 10
А лексичне зв'язування закриє одне з найбільших джерел болю та розчарування для розробників, а також покращить продуктивність за рахунок оптимізації на рівні js-движка.


Якщо ви хочете спробувати стрілочні функції, то можете виконати вищезгадані приклади в консолі Firefox, який на даний момент (02.2014 FF28) майже повноцінно підтримує стрілочні функції (FF28 неправильно обчислює значення arguments).

Також ви можете спробувати стрілочні функції та інші можливості es6 в онлайн трансляторі Traceur.

Теги: Додати теги

Всім привіт! У цій статті ми розглянемо, що таке стрілочні функції ES6 і як їх використовувати .

Функції стрілки – це функції, які записуються за допомогою оператора "стрілка" (=>).

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

Let add = (x, y) => x + y;
console.log(add(5, 2));

В результаті виконання цієї функції в консолі побачимо число 7.

Спочатку, у круглих дужках ми передаємо аргументи, далі ставимо знак стрілочки, а потім пишемо код самої функції. У нашому випадку вона просто приймає два числа та складає їх. За ідеєю, це те саме, що й функція Expression в ES5 . Якщо ви використовуєте Babel або подібні компілятори, то, швидше за все, вони напишуть щось на зразок цього:

Var add = функція add(x, y) (
return x + y;
};

Якщо функція приймає лише один параметр, круглі дужки ставити необов'язково.

Let square = x => x * x;

Така функція приймає лише один аргумент і зводить передане число квадрат.

Функція без параметрів:

Let func = () => 77;

Якщо ваша функція містить кілька рядків, то, по-перше, потрібно використовувати фігурні дужки, а по-друге, обов'язково написати, що функція повертає, тобто. використовувати ключове слово return.

Let multiply = (x, y) => (
let result = x * y;
return result;
};

Якщо вам потрібно повернути літерал об'єкта, його потрібно обернути в круглі дужки:

Let getObject = () => ((brand: "BMW"));

Функція, що самовикликається, виглядає наступним чином.

Угору функція expression є syntactically compact alternative до regular function expression , але без його своїх bindings до цього , arguments , super , або New.target keywords. Рівень функцій expressions є ill suited as methods, and they cannot be used as constructors.

Syntax Basic syntax (param1, param2, …, paramN) => ( statements ) (param1, param2, …, paramN) => expression // equivalent to: => ( return expression; ) // Parentheses are optional when there"s тільки один parameter name: (singleParam) => ( statements ) singleParam => ( statements ) // The parameter list for function with no parameters should be written with pair of parentheses () => ( statements ) Advanced syntax // Взаємодія з функцією до відновленого об'єкта літературного дзвінка: params => ((foo: bar)) // Інші параметри і деякі параметри є supported (param1, param2, ...rest) => ( statements ) (param1 = defaultValue1, param2, …, paramN = defaultValueN) => ( statements ) // Destructuring within the parameter list is also supported var f = ( = , (x: c) = (x: a + b)) => a + b + c; f(); // 6 Description

Два фактори influenced до введення arrow функцій: потрібні для shorter функцій і behavior of the keyword.

Shorter functions var elements = ["Hydrogen", "Helium", "Lithium", "Beryllium"]; / / Цей стан returns the array: elements.map (function (element) ( return element.length; )); // Регулярна функція може бути написана як arrow функція нижче elements.map((element) => ( return element.length; )); // // Якщо є лише один parameter, ми можемо remove the surrounding parentheses elements.map (element => ( return element.length; )); // // Коли тільки станція в arrow функції є `return`, ми можемо remove `return` and remove // ​​surrounding curly brackets elements.map(element => element.length); // // У цьому випадку, тому що тільки потрібно, щоб length properties, ми можемо використовувати деструктивний parameter: // Notice that the `length` corresponds to the property we want to get whereas the // obviously non-special `lengthFooBArX` is Just the name of variable which can be changed // to any valid variable name you want elements.map ((( length:lengthFooBArX )) => lengthFooBArX); // // Цей параметр деструкції вимірювання може бути написаний як білий. However, note that in // Цей example we не є assigning `length` value to the made up property. Instead, literal name // itself of the variable `length` is used as the property we want to retrieve from the object. elements.map (((length)) => length); // No separate this

Після функцій, новим новим функцією було визначено, що його ця величина базується на тому, що функція була названа:

  • Новий об'єкт у випадку конструктора.
  • undefined in strict mode function calls.
  • Основний об'єкт, якщо функція була названа як "object method".

Це зроблено для того, щоб бути бездоганним з object-oriented style of programming.

Function Person() ( // The Person() constructor defines `this` as instance of itself. this.age = 0; setInterval(function growUp() ( // In non-strict mode, the growUp() function defines ` this` // як global object (because it"s where growUp() is executed.), // which is different from the `this` // defined by the Person() constructor. this.age++; ), 1000) ;) var p = new Person();

У ECMAScript 3/5, ця сума була спрямована на визначення значення в цьому параметрі, що може бути заблоковано.

Function Person() ( var that = this; that.age = 0; setInterval(function growUp() ( // The callback refers to the `that` variable of which // the value is the expected object. that.age++; )) , 1000);) "use strict"; var obj = (a: 10); Object.defineProperty(obj, "b", ( get: () => ( console.log(this.a, typeof this.a, this)); // undefined "undefined" Window (...) (or the global object) return this.a + 10; // represents global object "Window", therefore "this.a" returns "undefined")));

Use of the new operator

Arrow functions cannot be used as constructors and will throw an error when used with new .

Var Foo = () => (); var foo = New Foo (); //TypeError: Foo is not a constructor

Use of prototype property

Налаштування функцій не має prototype property.

Var Foo = () => (); console.log(Foo.prototype); // undefined

Use of the yield keyword

Вони не можуть бути використані в arrow functions's body (за винятком коли можливі з функціями, які не існують без нього).

Function body

Набір функцій може мати її "концентрація тіла" або звичайний "блоковий пристрій".

In a concise body, тільки expression is specified, which becomes the implicit return value. У блоці, ви повинні використовувати explicit return statement.

Var func = x => x * x; // concise body syntax, іmplied "return" var func = (x, y) => (return x + y;); // with block body, explicit "return" needed

Returning object literals

Keep в думці, що відновити об'єкт літералів, використовуючи концепцію тіла syntax params => (object:literal) не буде працювати як expected.

Var func = () => (foo: 1); // Calling func() returns undefined! var func = () => (foo: function() ()); // SyntaxError: Function statement requires a name

Це означає, що код всередині браузів (()) є послідовним як записи статей (і.е. foo is treated like a label, no key in an object literal).

Ви повинні загорнути object literal in parentheses:

Var func = () => ((foo: 1));

Line breaks

У ході функцій може бути не визначено line break між його параметрами і його арrow.

Var func = (a, b, c) => 1; // SyntaxError: expected expression, got "=>"

Хоча, це може бути спрямований на те, щоб підняти лінію перерви після шипування або використання parentheses/braces, щоб бути вільним, щоб забезпечити, що code stays pretty and fluffy. Ви можете також брати line breaks між argumentами.

Var func = (a, b, c) => 1; var func = (a, b, c) => (1); var func = (a, b, c) => (return 1); var func = (a, b, c) => 1; // no SyntaxError thrown

Parsing order

Більше, як arrow in arrow function не є оператором, arrow функцій має особливі віртуальні правила, що interact differently with operator precedence compared to regular functions.

Let callback; callback = callback | function() (); // ok callback = callback | () => (); // SyntaxError: invalid arrow-function arguments callback = callback || (() => ()); // ok

More examples // An empty arrow function returns undefined let empty = () => (); (() => "foobar")(); // Returns "foobar" // (Це є безпосереднім Invoked Function Expression) var simple = a => a > 15 ? 15: a; simple(16); // 15 simple(10); // 10 let max = (a, b) => a > b? a: b; // Easy array filtering, mapping, ... var arr =; var sum = arr.reduce((a, b) => a + b); // 66 var even = arr.filter(v => v % 2 == 0); // var double = arr.map (v => v * 2); // // More concise promise chains promise.then(a => ( // ... )).then(b => ( // ... )); // Parameterless arrow functions that are visually easier to parse setTimeout(() => ( console.log("I happen sooner"); setTimeout(() => ( // deeper code console.log("I happen later") ; ), 1); Specifications Specification Status Comment
ECMAScript 2015 (6th Edition, ECMA-262)
Standard Initial definition.
ECMAScript Latest Draft (ECMA-262)
Definition of "Arrow Function Definitions" в цій specification.
Draft
Browser compatibility

Компактність таблиці на цій сторінці генерується з структурованих даних. Якщо ви знайдете, щоб дізнатися про ваші дані, клацніть на https://github.com/mdn/browser-compat-data і пишуть.

Update compatibility data on GitHub

Desktop Mobile Server


Internet Explorer Opera Safari Android webview Chrome для Android Firefox для Android Opera для Android Safari on iOS Samsung Internet Node.jsArrow функцій Trailing comma in parameters
Chrome Full support 45Edge Full support YesFirefox Full support 22

Notes

Full support 22

Notes

Notes Prior to Firefox 39, line terminator (\n) був incorrectly allowed after arrow function arguments. Це має бути fixed до conform to ES2015 specification and code like () \n =>
IE No support NoOpera Full support 32Safari Full support 10WebView Android Full support 45Chrome Android Full support 45Firefox Android Full support 22

Notes

Full support 22

Notes

Notes Ініційна реалізація arrow функцій в Firefox створена для них автоматично strict. Це буде змінено як Firefox 24. За допомогою "використання"; is now required. Notes Prior to Firefox 39, line terminator (\n) був incorrectly allowed after arrow function arguments. Це буде fixed до conform to ES2015 specification and code like () \n => () will now throw a SyntaxError в цій і останньої версії.
Opera Android Full support 32Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support Yes
Chrome Full support 58Edge?Firefox Full support 52IE No support NoOpera Full support 45Safari?WebView Android Full support 58Chrome Android Full support 58Firefox Android Full support 52Opera Android Full support 43Safari iOS?Samsung Internet Android Full support 7.0nodejs Full support Yes
Legend Full support Full support No support No support Compatibility unknown Compatibility unknown See implementation notes. See implementation notes.

Подібні публікації