Вступление
Если вы разработчик JavaScript, вы можете знать, что JavaScript соответствует стандартам ECMAScript (ES). Спецификации ES6 или ECMAScript 2015 представили некоторые революционные спецификации для JavaScript, такие как функции стрелок, классы, операторы Rest и Spread, Promises, let и const и т. Д.
В этом руководстве мы сосредоточимся на функциях стрелок, которые очень сбивают с толку и пугают новичков в JavaScript.
Синтаксис стрелочной функции
Как мы знаем, функция ES5 имеет следующий синтаксис:
function square(a) {
return a * a;
}
В ES6 мы можем написать ту же функцию с помощью только одной строчки кода:
let square = (a) => { return a * a; }
Более того, если в теле функции есть только один оператор, который он
возвращает, мы можем пропустить фигурные скобки {}
и оператор return
let square = (a) => a * a
Кроме того, если функция принимает только один параметр, мы можем даже
пропустить фигурные скобки ()
вокруг него:
let square = a => a * a
С другой стороны, если функция не принимает никаких параметров, мы можем написать это так:
let results = () => { /* ...some statements */ };
Нам нужно писать меньше кода с этим синтаксисом, обеспечивая при этом ту же функциональность, которая может помочь очистить и упростить ваш код.
Преимущество этого синтаксиса наиболее заметно при использовании в обратных вызовах. Итак, подробный и трудный для понимания фрагмент кода вроде этого:
function getRepos() {
return fetch('https://api.github.com/users/stackabuse/repos')
.then((response) => {
return response.json();
}).then((response) => {
return response.data;
}).then((repos) => {
return repos.filter((repo) => {
return repo.created_at > '2019-06-01';
});
}).then((repos) => {
return repos.filter((repo) => {
return repo.stargazers_count > 1;
});
});
}
можно свести к следующему, используя стрелочные функции:
function getRepos() {
return fetch('https://api.github.com/users/stackabuse/repos')
.then(response => response.json())
.then(response => response.data)
.then(repos => repos.filter(repo => repo.created_at > '2019-06-01'))
.then(repos => repos.filter(repo => repo.stargazers_count > 1));
}
Преимущества стрелочных функций
Есть два основных преимущества использования стрелочных функций.
Во-первых, это более короткий синтаксис и, следовательно, требует меньше
кода. Основное преимущество заключается в том, что он устраняет
несколько болевых точек, связанных с оператором this
Нет привязки оператора this
В отличие от других объектно-ориентированных языков программирования, в
JavaScript (перед стрелочными функциями) каждая функция определила свою
ссылку на this
и это зависит от того, как функция была вызвана. Если
у вас есть опыт работы с современными языками программирования, такими
как Java, Python, C # и т. Д., Оператор this
или self
внутри метода
относится к объекту, который вызвал метод, а не к тому , как этот
метод вызывается.
Программисты всегда жаловались, что использовать this
в JavaScript
слишком сложно. Это вызывает большую путаницу в сообществе JavaScript и
в некоторых случаях вызывает непреднамеренное поведение кода.
Чтобы лучше понять преимущества стрелочных функций, давайте сначала
разберемся, как this
работает в ES5.
Оператор this в ES5
Значение
this
определяется контекстом выполнения некоторой функции, которая в простых выражениях средства , как называется функция.
Что еще больше усугубляет путаницу, так это то, что каждый раз, когда вызывается одна и та же функция, контекст выполнения может быть другим.
Попробуем разобраться в этом на примере:
function test() {
console.log(this);
}
test();
Вывод этой программы в консоли браузера будет следующим:
Window {...}
Поскольку мы вызывали test()
из глобального контекста, this
относится к глобальному объекту, который в браузерах Window
Каждая
глобальная переменная, которую мы создаем, прикрепляется к этому
глобальному объекту Window
.
Например, если вы запустите следующий код в консоли браузера:
let greetings = 'Hello World!';
console.log(greetings);
console.log(window.greetings)
вас встретит привет, мир! дважды:
Hello World!
Hello World!
Как мы видим, greetings
глобальной переменной прикреплено к window
глобального объекта.
Давайте рассмотрим другой пример с функцией-конструктором:
function Greetings(msg) {
this.msg = msg;
};
let greetings = Greetings('Hello World!');
console.log(greetings);
В консоли мы получим следующее сообщение:
undefined
что имеет смысл, потому что мы вызываем Greetings()
в window
и, как
и в предыдущем примере, this
относится к глобальному объекту window
а this.msg
добавил свойство msg
к объекту window
Мы можем проверить это, если запустим:
window.msg
Нас встретят:
Hello World!
Но если мы используем new
при создании объекта Greetings
let greetings = new Greetings('Hello World!');
console.log(greetings.msg);
Нас встретят:
Hello World!
Мы видим, что на this
оператор this не относится к глобальному объекту
window
Эту магию творит new
Оператор new
создает пустой объект и
заставляет this
ссылаться на этот пустой объект.
Надеюсь, теперь вы уловили наше предыдущее заявление.
this
зависит от того, как вызывается функция.
Возьмем другой пример:
let greetings = {
msg: 'Hello World!',
greet: function(){
console.log(this.msg);
}
}
greetings.greet();
Если мы запустим этот код в консоли браузера, нас снова встретят:
Hello World!
Как вы думаете, почему на this
это не относится к объекту window
Неявная привязка:
this
ключевое слово привязано к объекту перед точкой.
В нашем примере это относится к объекту greetings
Но если мы присвоим переменной greetings.greet
let greetRef = greetings.greet;
greetRef();
Нас встретят:
undefined
Это что-нибудь объясняет? Помните вызов функции в контексте окна из предыдущего примера?
Поскольку мы вызываем greetRef()
в window
, в данном случае this
относится к window
и мы знаем, что у него нет свойства msg
Давайте рассмотрим более сложный сценарий использования анонимной функции:
let factory = {
items: [5, 1, 12],
double: function(){
return this.items.map(function(item, index){
let value = item*2;
console.log(`${value} is the double of ${this.items[index]}`);
return value;
});
}
};
Если мы вызовем factory.double()
мы получим следующую ошибку:
Uncaught TypeError: Cannot read property '0' of undefined
at <anonymous>
at Array.map (<anonymous>)
Ошибка указывает на то, что this.items
не определен, что означает, что
this
внутри анонимной функции map()
не относится к объекту factory
Таковы причины , почему мы называем это значение this
определяется
контекстом исполнения функции. Есть более сложные примеры по этой
болевой точке, которые выходят за рамки этого руководства.
Есть несколько способов обойти эту проблему, например передать this
или использовать bind()
. Но использование этих обходных путей делает
код сложным и излишне раздутым.
К счастью, в ES6 стрелочные функции более предсказуемы с точки зрения
ссылки на ключевое слово this
Оператор this и стрелочные функции в ES6
Во-первых, давайте преобразуем анонимную функцию внутри map()
в
стрелочную функцию:
let factory = {
items: [5, 1, 12],
double: function(){
return this.items.map((item, index) => {
let value = item*2;
console.log(`${value} is the double of ${this.items[index]}`);
return value;
});
}
};
Если мы вызовем factory.double()
нас встретит:
10 is the double of 5
2 is the double of 1
24 is the double of 12
[10, 2, 24]
Как мы видим, поведение this
внутри стрелочной функции вполне
предсказуемо. В стрелочных функциях this
всегда будет принимать
значение извне. На самом деле стрелочная функция даже this
не имеет.
Если мы обратимся к this
где-нибудь в стрелочной функции, поиск будет
выполнен точно так же, как если бы это была обычная переменная - во
внешней области. Мы также называем это лексической
областью
.
В нашем примере this
внутри стрелочной функции имеет то же значение,
что и this
снаружи, то есть в методе double()
. Итак, this.items
в
стрелочной функции совпадает с this.items
в методе double()
и это
factory.items
.
Стрелочные функции нельзя вызывать с помощью оператора
new
Поскольку у стрелочной функции нет this
ключевого слова, очевидно, что
они не могут поддерживать new
оператор.
Заключение
Стрелочные функции могут сбивать с толку, но очень полезно сделать код
более предсказуемым с помощью лексической области видимости ключевого
слова this
Он также удобен для пальцев, поскольку позволяет набирать
меньше кода.
Как разработчик JavaScript, вам должно быть комфортно использовать его, поскольку он широко используется с различными интерфейсными фреймворками / библиотеками, такими как React, Angular и т. Д. Я надеюсь, что этот учебник поможет вам решить, когда и как реализовать стрелочные функции в вашем будущие проекты.