Разработка сайтов в Кировском, ДНР. Стрелочные функции в JavaScript: как использовать толстый и лаконичный синтаксис

Узнайте все о стрелочных функциях JavaScript. Мы покажем вам, как использовать синтаксис стрелок ES6, а также некоторые распространенные ошибки, о которых вам нужно знать при использовании стрелочных функций в вашем коде. Вы увидите множество примеров, иллюстрирующих, как они работают.

Стрелочные функции JavaScript появились с выпуском ECMAScript 2015, также известного как ES6. Из-за лаконичного синтаксиса и обработки ключевого слова this стрелочные функции быстро стали любимой функцией среди разработчиков.

Синтаксис стрелочной функции: переписывание обычной функции

Функции похожи на рецепты, в которых вы храните полезные инструкции для выполнения того, что вам нужно сделать в вашей программе, например, выполнить действие или вернуть значение. Вызывая свою функцию, вы выполняете шаги, включенные в ваш рецепт. Вы можете делать это каждый раз, когда вызываете эту функцию, без необходимости переписывать рецепт снова и снова.

Вот стандартный способ объявить функцию, а затем вызвать ее в JavaScript:

// function declaration

function sayHiStranger () {

return 'Hi, stranger!'

}

// call the function

sayHiStranger ()

Вы также можете написать ту же функцию в виде функционального выражения, например:

const sayHiStranger = function () {

return 'Hi, stranger!'

}

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

const sayHiStranger = () => 'Hi, stranger’

Преимущества этого включают в себя:

всего одна строка кода

нет functionключевого слова

нет returnключевого слова

и без фигурных скобок {}

В JavaScript функции являются «гражданами первого класса». Вы можете хранить функции в переменных, передавать их другим функциям в качестве аргументов и возвращать их из других функций в виде значений. Вы можете сделать все это, используя стрелочные функции JavaScript.

Синтаксис без скобок

В приведенном выше примере функция не имеет параметров. В этом случае вы должны добавить набор пустых скобок перед символом () толстой стрелки (). =>То же самое верно, когда у вас есть более одного параметра:

const getNetflixSeries = (seriesName, releaseDate) => `The ${seriesName} series was released in ${releaseDate}`

// call the function

console.log (getNetflixSeries ('Bridgerton’, '2020'))

// output: The Bridgerton series was released in 2020

Однако с одним параметром вы можете пойти дальше и опустить круглые скобки (вы не обязаны, но можете):

const favoriteSeries = seriesName => seriesName === «Bridgerton»? «Let’s watch it»: «Let’s go out»

// call the function

console.log (favoriteSeries («Bridgerton»))

// output: «Let’s watch it»

Однако будьте осторожны. Если, например, вы решили использовать параметр по умолчанию, вы должны заключить его в круглые скобки:

// with parentheses: correct

const bestNetflixSeries = (seriesName = «Bridgerton») => `${seriesName} is the best`

// outputs: «Bridgerton is the best»

console.log (bestNetflixSeries ())

// no parentheses: error

const bestNetflixSeries = seriesName = «Bridgerton» => `${seriesName} is the best`

// Uncaught SyntaxError: invalid arrow-function arguments (parentheses around the arrow-function may help)

И только потому, что вы можете, не означает, что вы должны. Кайл Симпсон (известный как You Don’t Know JS), смешанный с легким сарказмом из лучших побуждений, изложил в этой блок-схеме свои мысли об исключении круглых скобок.

Неявный возврат

Когда у вас есть только одно выражение в теле функции, вы можете сделать синтаксис стрелки ES6 еще более кратким. Вы можете сохранить все в одной строке, удалить фигурные скобки и покончить с returnключевым словом.

Вы только что видели, как работают эти изящные однострочники в приведенных выше примерах. Вот еще один пример, на всякий случай. Функция orderByLikes () делает то, что написано на банке: она возвращает массив объектов серии Netflix, упорядоченных по наибольшему количеству лайков:

// using the JS sort () function to sort the titles in descending order

// according to the number of likes (more likes at the top, fewer at the bottom

const orderByLikes = netflixSeries.sort ((a, b) => b.likes — a.likes)

// call the function

// output: the titles and the n. of likes in descending order

console.log (orderByLikes)

Это круто, но следите за удобочитаемостью кода, особенно при построении последовательности стрелочных функций с использованием однострочников и синтаксиса стрелок ES6 без скобок, как в этом примере:

const greeter = greeting => name => `${greeting}, ${name}! `

Что там происходит? Попробуйте использовать обычный синтаксис функции:

function greeter (greeting) {

return function (name) {

return `${greeting}, ${name}! `

}

}

Теперь вы можете быстро увидеть, как внешняя функция greeterимеет параметр greetingи возвращает анонимную функцию. Эта внутренняя функция, в свою очередь, имеет вызванный параметр nameи возвращает строку, используя значение как, так greetingи name. Вот как вы можете вызвать функцию:

const myGreet = greeter ('Good morning’)

console.log (myGreet ('Mary’))

// output:

«Good morning, Mary!»

Остерегайтесь этих ошибок неявного возврата

Когда ваша стрелочная функция JavaScript содержит более одного оператора, вам нужно заключить их все в фигурные скобки и использовать returnключевое слово.

В приведенном ниже коде функция создает объект, содержащий заголовок и сводку нескольких сериалов Netflix (обзоры Netflix взяты с веб-сайта Rotten Tomatoes):

const seriesList = netflixSeries.map (series => {

const container = {}

container.title = series.name

container.summary = series.summary

// explicit return

return container

})

Стрелочная функция внутри.map () функции развивается из серии операторов, в конце которых она возвращает объект. Это делает неизбежным использование фигурных скобок вокруг тела функции.

Кроме того, поскольку вы используете фигурные скобки, неявный возврат невозможен. Вы должны использовать returnключевое слово.

Если ваша функция возвращает литерал объекта, используя неявный возврат, вам необходимо заключить объект в круглые скобки. Если этого не сделать, возникнет ошибка, поскольку движок JavaScript ошибочно интерпретирует фигурные скобки литерала объекта как фигурные скобки функции. И, как вы только что заметили выше, когда вы используете фигурные скобки в стрелочной функции, вы не можете опустить ключевое слово return.

Более короткая версия предыдущего кода демонстрирует такой синтаксис:

// Uncaught SyntaxError: unexpected token: ':'

const seriesList = netflixSeries.map (series => { title: series.name }) ;

// Works fine

const seriesList = netflixSeries.map (series => ({ title: series.name }));

Вы не можете называть стрелочные функции

Функции, у которых нет идентификатора имени между functionключевым словом и списком параметров, называются анонимными функциями. Вот как выглядит обычное выражение анонимной функции:

const anonymous = function () {

return 'You can\'t identify me!'

}

Все стрелочные функции являются анонимными:

const anonymousArrowFunc = () => 'You can\'t identify me!'

Начиная с ES6, переменные и методы могут вывести имя анонимной функции из ее синтаксической позиции, используя ее nameсвойство. Это позволяет идентифицировать функцию при проверке ее значения или сообщении об ошибке.

Проверьте это, используя anonymousArrowFunc:

console.log (anonymousArrowFunc.name)

// output: «anonymousArrowFunc»

Имейте в виду, что это предполагаемое nameсвойство существует только тогда, когда анонимная функция назначается переменной, как в приведенных выше примерах. Если вы используете анонимную функцию в качестве обратного вызова, вы теряете эту полезную функцию. Это показано в приведенной ниже демонстрации, где анонимная функция внутри.setInterval () метода не может воспользоваться nameсвойством:

let counter = 5

let countDown = setInterval (() => {

console.log (counter)

counter—

if (counter === 0) {

console.log («I have no name!!»)

clearInterval (countDown)

}

}, 1000)

И это еще не все. Это предполагаемое nameсвойство по-прежнему не работает как надлежащий идентификатор, который вы можете использовать для ссылки на функцию изнутри — например, для рекурсии, отмены привязки событий и т. д.

Внутренняя анонимность стрелочных функций побудила Кайла Симпсона выразить свое мнение о них следующим образом:

Поскольку я не думаю, что анонимные функции — это хорошая идея для частого использования в ваших программах, я не сторонник использования =>формы функции стрелки. — Вы не знаете JS

Как стрелочные функции обрабатывают ключевое thisслово

Самое важное, что нужно помнить о стрелочных функциях, — это то, как они обрабатывают thisключевое слово. В частности, thisключевое слово внутри стрелочной функции не восстанавливается.

Чтобы проиллюстрировать, что это значит, посмотрите демо ниже:

[codepen_embed height=«300″ default_tab="html, result» slug_hash=«qBqgBmR» user="SitePoint"] См. Pen

JS this в функциях стрелок от SitePoint (@SitePoint)

на CodePen.[/codepen_embed]

Вот кнопка. Нажатие на кнопку запускает обратный счетчик от 5 до 1, который отображается на самой кнопке.

...

const startBtn = document.querySelector («.start-btn») ;

startBtn.addEventListener ('click’, function () {

this.classList.add ('counting’)

let counter = 5;

const timer = setInterval (() => {

this.textContent = counter

counter —

if (counter < 0) {

this.textContent = 'THE END!'

this.classList.remove ('counting’)

clearInterval (timer)

}

}, 1000)

})

Обратите внимание, что обработчик событий внутри.addEventListener () метода представляет собой обычное выражение анонимной функции, а не функцию стрелки. Почему? Если вы войдете thisвнутрь функции, вы увидите, что она ссылается на элемент кнопки, к которому был прикреплен слушатель, что именно то, что ожидается и что необходимо для того, чтобы программа работала по плану:

startBtn.addEventListener ('click’, function () {

console.log (this)

...

})

Вот как это выглядит в консоли инструментов разработчика Firefox:

журнал ключевого слова this внутри обычного выражения анонимной функции, которое обрабатывает событие нажатия кнопки

Однако попробуйте заменить обычную функцию стрелочной функцией, например:

startBtn.addEventListener ('click’, () => {

console.log (this)

...

})

Теперь thisбольше не ссылается на кнопку. Вместо этого он ссылается на Windowобъект:

объект окна, на который ссылается это ключевое слово внутри функции стрелки JavaScript, переданной прослушивателю событий

Это означает, что если вы хотите использовать thisдля добавления класса к кнопке, например, после ее нажатия, ваш код не будет работать:

// change button’s border’s appearance

this.classList.add ('counting’)

Вот сообщение об ошибке в консоли:

this.classList не определен

Когда вы используете стрелочную функцию в JavaScript, значение thisключевого слова не восстанавливается. Он наследуется от родительской области (это называется лексической областью видимости). В данном конкретном случае рассматриваемая стрелочная функция передается в качестве аргумента startBtn.addEventListener () методу, находящемуся в глобальной области видимости. Следовательно, thisвнутренний обработчик функции также привязан к глобальной области видимости, то есть к Windowобъекту.

Итак, если вы хотите thisсослаться на кнопку запуска в программе, правильный подход — использовать обычную функцию, а не функцию стрелки.

Анонимные стрелочные функции

Следующее, на что следует обратить внимание в приведенной выше демонстрации, — это код внутри.setInterval () метода. Здесь вы также найдете анонимную функцию, но на этот раз это функция стрелки. Почему?

Обратите внимание, каким было бы значение this, если бы вы использовали обычную функцию:

const timer = setInterval (function () {

console.log (this)

...

}, 1000)

Будет ли это buttonэлемент? Нисколько. Это был бы Windowобъект!

Использование обычной функции внутри setInterval () изменяет ссылку ключевого слова this с кнопки на объект Window.

На самом деле контекст изменился, поскольку thisтеперь он находится внутри несвязанной или глобальной функции, которая передается в качестве аргумента.setInterval (). Поэтому значение thisключевого слова также изменилось, так как теперь оно привязано к глобальной области.

Распространенным приемом в этой ситуации было включение другой переменной для хранения значения thisключевого слова, чтобы оно продолжало ссылаться на ожидаемый элемент — в данном случае на buttonэлемент:

const that = this

const timer = setInterval (function () {

console.log (that)

...

}, 1000)

Вы также можете использовать.bind () для решения проблемы:

const timer = setInterval (function () {

console.log (this)

...

}.bind (this), 1000)

Со стрелочными функциями проблема вообще исчезает. Вот какое значение thisимеет значение при использовании функции стрелки:

const timer = setInterval (() => {

console.log (this)

...

}, 1000)

значение this внутри функции стрелки, переданной в setInterval ()

На этот раз консоль регистрирует кнопку, что нам и нужно. На самом деле программа собирается изменить текст кнопки, поэтому ей нужно thisобратиться к buttonэлементу:

const timer = setInterval (() => {

console.log (this)

// the button’s text displays the timer value

this.textContent = counter

}, 1000)

Стрелочные функции не имеют собственного thisконтекста. Они наследуют значение thisот родителя, и именно из-за этой особенности они делают отличный выбор в ситуациях, подобных приведенной выше.

Стрелочные функции JavaScript не всегда подходят для работы

Стрелочные функции — это не просто модный новый способ написания функций в JavaScript. У них есть свои ограничения, а это значит, что бывают случаи, когда вы не хотите их использовать. Обработчик кликов в предыдущей демонстрации является примером, но не единственным. Рассмотрим еще несколько.

Стрелочные функции как методы объекта

Стрелочные функции не работают как методы для объектов. Вот пример.

Рассмотрим этот netflixSeriesобъект, который имеет некоторые свойства и пару методов. Вызов console.log (netflixSeries.getLikes ())должен напечатать сообщение с текущим количеством лайков, а вызов console.log (netflixSeries.addLike ())должен увеличить количество лайков на единицу, а затем вывести новое значение с сообщением благодарности на консоли:

const netflixSeries = {

title: 'After Life’,

firstRealease: 2019,

likes: 5,

getLikes: () => `${this.title} has ${this.likes} likes`,

addLike: () => {

this.likes++

return `Thank you for liking ${this.title}, which now has ${this.likes} likes`

}

}

Вместо этого вызов.getLikes () метода возвращает «undefined имеет лайки NaN», а вызов.addLike () метода возвращает «Спасибо, что понравился undefined, у которого теперь есть лайки NaN». Таким образом, this.titleи this.likesне удается ссылаться на свойства объекта titleи likesсоответственно.

Опять же, проблема заключается в лексической области действия стрелочных функций. Метод thisвнутри объекта ссылается на область родителя, которая в данном случае является Windowобъектом, а не самим родителем, то есть не netflixSeriesобъектом.

Решение, конечно же, заключается в использовании обычной функции:

const netflixSeries = {

title: 'After Life’,

firstRealease: 2019,

likes: 5,

getLikes () {

return `${this.title} has ${this.likes} likes`

},

addLike () {

this.likes++

return `Thank you for liking ${this.title}, which now has ${this.likes} likes`

}

}

// call the methods

console.log (netflixSeries.getLikes ())

console.log (netflixSeries.addLike ())

// output:

After Life has 5 likes

Thank you for liking After Life, which now has 6 likes

Стрелочные функции со сторонними библиотеками

Еще одна проблема, о которой следует знать, заключается в том, что сторонние библиотеки часто связывают вызовы методов, чтобы thisзначение указывало на что-то полезное.

Например, внутри обработчика событий jQuery thisвы получите доступ к элементу DOM, к которому был привязан обработчик:

$ ('body’).on ('click’, function () {

console.log (this)

})

//

 

Но если мы воспользуемся стрелочной функцией, которая, как мы видели, не имеет собственного thisконтекста, мы получим неожиданные результаты:

$ ('body’).on ('click’, () =>{

console.log (this)

})

// Window

Вот еще один пример с использованием Vue:

new Vue ({

el: app,

data: {

message: 'Hello, World!'

},

created: function () {

console.log (this.message) ;

}

})

// Hello, World!

Внутри createdхука thisпривязан к экземпляру Vue, поэтому «Hello, World!» отображается сообщение.

Однако, если мы используем функцию стрелки, thisона будет указывать на родительскую область, у которой нет messageсвойства:

new Vue ({

el: app,

data: {

message: 'Hello, World!'

},

created: function () {

console.log (this.message) ;

}

})

// undefined

Стрелочные функции не имеют argumentsобъекта

Иногда вам может понадобиться создать функцию с неопределенным количеством параметров. Например, допустим, вы хотите создать функцию, которая перечисляет ваши любимые сериалы Netflix, упорядоченные по предпочтениям. Однако вы еще не знаете, сколько серий вы собираетесь включить. JavaScript делает объект arguments доступным. Это массивоподобный объект (не полноценный массив), в котором хранятся значения, переданные функции при вызове.

Попробуйте реализовать эту функциональность с помощью стрелочной функции:

const listYourFavNetflixSeries = () => {

// we need to turn the arguments into a real array

// so we can use.map ()

const favSeries = Array.from (arguments)

return favSeries.map ((series, i) => {

return `${series} is my #${i +1} favorite Netflix series`

})

console.log (arguments)

}

console.log (listYourFavNetflixSeries ('Bridgerton’, 'Ozark’, 'After Life’))

При вызове функции вы получите следующее сообщение об ошибке: Uncaught ReferenceError: arguments is not defined. Это означает, что argumentsобъект недоступен внутри стрелочных функций. На самом деле, замена стрелочной функции обычной функцией делает свое дело:

const listYourFavNetflixSeries = function () {

const favSeries = Array.from (arguments)

return favSeries.map ((series, i) => {

return `${series} is my #${i +1} favorite Netflix series`

})

console.log (arguments)

}

console.log (listYourFavNetflixSeries ('Bridgerton’, 'Ozark’, 'After Life’))

// output:

[«Bridgerton is my #1 favorite Netflix series», «Ozark is my #2 favorite Netflix series», «After Life is my #3 favorite Netflix series»]

Итак, если вам нужен argumentsобъект, вы не можете использовать стрелочные функции.

Но что, если вы действительно хотите использовать стрелочную функцию для воспроизведения той же функциональности? Одна вещь, которую вы можете сделать, это использовать остальные параметры ES6 (...). Вот как вы могли бы переписать свою функцию:

const listYourFavNetflixSeries = (...seriesList) => {

return seriesList.map ((series, i) => {

return `${series} is my #${i +1} favorite Netflix series`

})

}

Вывод

Используя стрелочные функции, вы можете написать краткие однострочники с неявным возвратом и, наконец, забыть о старых хаках для решения привязки thisключевого слова в JavaScript. Стрелочные функции также отлично работают с методами массива, такими как.map (),.sort (),.forEach (),.filter () и.reduce (). Но помните: стрелочные функции не заменяют обычные функции JavaScript. Не забывайте использовать функции стрелок JavaScript только тогда, когда они являются правильным инструментом для работы.

Делитесь нашими материалами с друзьями!

 

 

Заказать разработку сайта