Давайте вернёмся к стрелочным функциям.
Стрелочные функции – это не просто «сокращение», чтобы меньше писать. У них есть ряд других полезных особенностей.
При написании JavaScript-кода часто возникают ситуации, когда нам нужно написать небольшую функцию, которая будет выполнена где-то ещё.
Например:
arr.forEach(func)–funcвыполняетсяforEachдля каждого элемента массива.setTimeout(func)–funcвыполняется встроенным планировщиком.- …и так далее.
Это очень в духе JavaScript – создать функцию и передать её куда-нибудь.
И в таких функциях мы обычно не хотим выходить из текущего контекста. Здесь как раз и полезны стрелочные функции.
У стрелочных функций нет «this»
Как мы помним из главы Методы объекта, "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.
Существует тонкая разница между стрелочной функцией => и обычной функцией, вызванной с .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 могла получить их.
Итого
Стрелочные функции:
- Не имеют
this. - Не имеют
arguments. - Не могут быть вызваны с
new. - (У них также нет
super, но мы про это не говорили. Про это будет в главе Наследование классов).
Всё это потому, что они предназначены для небольшого кода, который не имеет своего «контекста», выполняясь в текущем. И они отлично справляются с этой задачей!
Комментарии
<code>, для нескольких строк кода — тег<pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)