Стрелочные функции в JavaScript

Введение Если вы разработчик JavaScript, вы можете знать, что JavaScript соответствует стандартам ECMAScript [https://en.wikipedia.org/wiki/ECMAScript] (ES). Спецификации ES6 или ECMAScript 2015 представили некоторые революционные спецификации для JavaScript, такие как функции стрелок, классы, операторы Rest и Spread, обещания, let и const и т. Д. В этом руководстве мы сосредоточимся на функциях стрелок, которые очень сбивает с толку и пугает новичков в JavaScript. Стрелка F

Вступление

Если вы разработчик 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 и т. Д. Я надеюсь, что этот учебник поможет вам решить, когда и как реализовать стрелочные функции в вашем будущие проекты.

comments powered by Disqus