Кратко
Стрелочные функции:
- Не имеют
this
. - Не имеют
arguments
. - Не могут быть вызваны с
new
. - (У них также нет
super
, но мы про это не говорили. Про это будет в 0055 Прототипы, наследования
Всё это потому, что они предназначены для небольшого кода, который не имеет своего «контекста», выполняясь в текущем. И они отлично справляются с этой задачей!
Стрелочные функции, основы
Введение
Существует ещё один очень простой и лаконичный синтаксис для создания функций, который часто лучше, чем 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
, чтобы вернуть значение.
Повторяем стрелочные функции
Введение
Давайте вернёмся к стрелочным функциям.
Стрелочные функции – это не просто «сокращение», чтобы меньше писать. У них есть ряд других полезных особенностей.
При написании JavaScript-кода часто возникают ситуации, когда нам нужно написать небольшую функцию, которая будет выполнена где-то ещё.
Например:
arr.forEach(func)
–func
выполняетсяforEach
для каждого элемента массива.setTimeout(func)
–func
выполняется встроенным планировщиком.- …и так далее.
Это очень в духе JavaScript – создать функцию и передать её куда-нибудь.
И в таких функциях мы обычно не хотим выходить из текущего контекста. Здесь как раз и полезны стрелочные функции.
У стрелочных функций нет «this»
Как мы помним из 0047 Метод объекта this, у стрелочных функций нет this
. Если происходит обращение к this
, его значение берётся снаружи.
Например, мы можем использовать это для итерации внутри метода объекта:
let group = {
title: "Our Group",
students: ["John", "Pete", "Alice"],
showList() {
this.students.forEach(
student => alert(this.title + ': ' + student)
);_
}
};
group.showList();`
Здесь внутри forEach
использована стрелочная функция, таким образом this.title
в ней будет иметь точно такое же значение, как в методе showList
: group.title
.
Если бы мы использовали «обычную» функцию, была бы ошибка:
let group = {
title: "Our Group",
students: ["John", "Pete", "Alice"],
showList() {
this.students.forEach(function(student) {
// Error: Cannot read property 'title' of undefined
alert(this.title + ': ' + student)
}
);_ } };
group.showList();`
Ошибка возникает потому, что forEach
по умолчанию выполняет функции с this
, равным undefined
, и в итоге мы пытаемся обратиться к undefined.title
.
Это не влияет на стрелочные функции, потому что у них просто нет this
.
Стрелочные функции нельзя использовать с new
Отсутствие this
естественным образом ведёт к другому ограничению: стрелочные функции не могут быть использованы как конструкторы. Они не могут быть вызваны с new
.
Стрелочные функции VS bind
Существует тонкая разница между стрелочной функцией => и обычной функцией, вызванной с .bind(this)
:
.bind(this)
создаёт «связанную версию» функции.- Стрелка => ничего не привязывает. У функции просто нет
this
. При получении значенияthis
– оно, как обычная переменная, берётся из внешнего лексического окружения.
Стрелочные функции не имеют «arguments»
У стрелочных функций также нет переменной arguments
.
Это отлично подходит для декораторов, когда нам нужно пробросить вызов с текущими this
и arguments
.
Например, defer(f, ms)
принимает функцию и возвращает обёртку над ней, которая откладывает вызов на ms
миллисекунд:
function defer(f, ms) {
return function() {
setTimeout(() => f.apply(this, arguments), ms)
};
}
function sayHi(who) {
alert('Hello, ' + who);
}
let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("John"); // выводит "Hello, John" через 2 секунды`
То же самое без стрелочной функции выглядело бы так:
function defer(f, ms) {
return function(...args) {
let ctx = this;
setTimeout(function() {
return f.apply(ctx, args);
},
ms);
}; }`
Здесь мы были вынуждены создать дополнительные переменные args
и ctx
, чтобы функция внутри setTimeout
могла получить их.