ES6 Arrow Functions: Sintaxa grasă și concisă în JavaScript

În acest articol, veți afla totul despre sintaxa funcțiilor săgeată din JavaScript – inclusiv câteva dintre problemele de care trebuie să fiți conștienți atunci când folosiți funcțiile săgeată în codul dumneavoastră. Veți vedea o mulțime de exemple care ilustrează modul în care acestea funcționează.

Funcțiile săgeată au fost introduse în JavaScript odată cu lansarea ECMAScript 2015, cunoscut și sub numele de ES6. Sintaxa lor concisă și modul în care gestionează cuvântul cheie this se numără printre principalele caracteristici care au contribuit la succesul considerabil al funcțiilor arrow în rândul dezvoltatorilor.

Turning a Pre-ES6 Function into an Arrow Function

Ați putea considera funcțiile ca un fel de rețetă în care stocați instrucțiuni utile pentru a realiza ceva ce aveți nevoie să faceți în programul dumneavoastră, cum ar fi efectuarea unei acțiuni sau returnarea unei valori. Prin apelarea funcției, executați pașii incluși în rețeta dvs. și puteți face acest lucru de fiecare dată când apelați funcția respectivă, fără a fi nevoie să rescrieți rețeta din nou și din nou.

Iată un mod standard de a declara o funcție și apoi de a o apela în JavaScript:

// function declarationfunction sayHiStranger() { return 'Hi, stranger!'}// call the functionsayHiStranger()

De asemenea, puteți scrie aceeași funcție ca o expresie de funcție, astfel:

const sayHiStranger = function () { return 'Hi, stranger!'}

Funcțiile săgeată sunt întotdeauna expresii. Iată cum ați putea rescrie funcția de mai sus folosind notația săgeată grasă:

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

Beneficiile acestei metode includ:

  • doar o singură linie de cod
  • fără cuvinte cheie function
  • fără cuvinte cheie return
  • fără paranteze curbe {}

În JavaScript, funcțiile sunt „cetățeni de primă clasă”. Adică, puteți stoca funcții în variabile, le puteți transmite altor funcții ca argumente și le puteți returna de la alte funcții ca valori. Puteți face toate acestea folosind funcții săgeată.

Să trecem în revistă diferitele moduri în care puteți scrie funcții săgeată.

Sintaxa fără parametri

În exemplul de mai sus, funcția nu are parametri. În acest caz, trebuie să adăugați un set de paranteze goale () înainte de simbolul săgeată grasă (=>). Același lucru este valabil și atunci când aveți mai mult de un parametru:

const getNetflixSeries = (seriesName, releaseDate) => `The ${seriesName} series was released in ${releaseDate}`// call the functionconsole.log(getNetflixSeries('Bridgerton', '2020') )// output: The Bridgerton series was released in 2020

Cu un singur parametru, totuși, puteți merge mai departe și să omiteți parantezele (nu este obligatoriu, dar puteți):

const favoriteSeries = seriesName => seriesName === "Bridgerton" ? "Let's watch it" : "Let's go out"// call the functionconsole.log(favoriteSeries("Bridgerton"))// output: "Let's watch it"

Atenție, totuși. Dacă, de exemplu, decideți să folosiți un parametru implicit, trebuie să îl înfășurați în paranteze:

// with parentheses: correctconst bestNetflixSeries = (seriesName = "Bridgerton") => `${seriesName} is the best`// outputs: "Bridgerton is the best"console.log(bestNetflixSeries())// no parentheses: errorconst bestNetflixSeries = seriesName = "Bridgerton" => `${seriesName} is the best`// Uncaught SyntaxError: invalid arrow-function arguments (parentheses around the arrow-function may help)

Și doar pentru că puteți, nu înseamnă că trebuie. Amestecat cu un pic de sarcasm ușor și bine intenționat, Kyle Simpson (de la You Don’t Know JS fame) a pus gândurile sale cu privire la omiterea parantezelor în această diagramă de flux.

Returnare implicită

Când aveți doar o singură expresie în corpul funcției, puteți păstra totul pe o singură linie, eliminați parantezele curly braces și renunțați la cuvântul cheie return. Tocmai ați văzut cum funcționează aceste linii unice ingenioase în exemplele de mai sus. Iată încă un exemplu, pentru o măsură bună. Funcția orderByLikes() face ceea ce scrie pe cutie: adică returnează o matrice de obiecte de seriale Netflix ordonate în funcție de cel mai mare număr de like-uri:

// 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 bottomconst orderByLikes = netflixSeries.sort( (a, b) => b.likes - a.likes )// call the function // output:the titles and the n. of likes in descending orderconsole.log(orderByLikes)

Este grozav, dar fiți atenți la lizibilitatea codului dumneavoastră – mai ales atunci când secvențiați o grămadă de funcții săgeată folosind one-liners și sintaxa fără paranteze, ca în acest exemplu:

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

Ce se întâmplă acolo? Încercați să folosiți sintaxa funcțiilor obișnuite:

function greeter(greeting) { return function(name) { return `${greeting}, ${name}!` }} 

Acum, puteți vedea rapid cum funcția exterioară greeter are un parametru, greeting, și returnează o funcție anonimă. La rândul său, această funcție interioară are un parametru numit name și returnează un șir de caractere folosind atât valoarea lui greeting, cât și pe cea a lui name. Iată cum puteți apela funcția:

const myGreet = greeter('Good morning')console.log( myGreet('Mary') ) // output: "Good morning, Mary!" 

Atenție la aceste probleme de returnare implicită

Când funcția săgeată conține mai mult de o instrucțiune, trebuie să le înfășurați pe toate între paranteze curly brace și să folosiți cuvântul cheie return. În codul de mai jos, funcția construiește un obiect care conține titlul și rezumatul câtorva seriale Netflix (recenziile Netflix sunt preluate de pe site-ul Rotten Tomatoes) :

const seriesList = netflixSeries.map( series => { const container = {} container.title = series.name container.summary = series.summary // explicit return return container} )

Funcția săgeată din interiorul funcției .map() se dezvoltă pe o serie de declarații, la sfârșitul cărora returnează un obiect. Acest lucru face ca utilizarea parantezelor curbe în jurul corpului funcției să fie inevitabilă. De asemenea, având în vedere că folosiți acolade curly braces, un return implicit nu este o opțiune. Trebuie să folosiți cuvântul cheie return.

Dacă funcția dvs. de săgeată returnează un literal de obiect folosind return implicit, trebuie să înfășurați obiectul în interiorul parantezelor rotunde. Dacă nu faceți acest lucru, se va produce o eroare, deoarece motorul JS analizează în mod eronat parantezele curbe ale literalului obiectului ca fiind parantezele curbe ale funcției. Și, după cum tocmai ați observat mai sus, atunci când folosiți paranteze curbe într-o funcție săgeată, nu puteți omite cuvântul cheie return.

Această sintaxă este demonstrată în versiunea mai scurtă a codului anterior:

// Uncaught SyntaxError: unexpected token: ':'const seriesList = netflixSeries.map(series => { title: series.name });// Works fineconst seriesList = netflixSeries.map(series => ({ title: series.name }));

You Can’t Name Arrow Functions

Funcțiile care nu au un identificator de nume între cuvântul cheie function și lista de parametri se numesc funcții anonime. Iată cum arată o expresie obișnuită a unei funcții anonime:

const anonymous = function() { return 'You can\'t identify me!' }

Funcțiile săgeată sunt toate funcții anonime:

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

Începând cu ES6, variabilele și metodele pot deduce numele unei funcții anonime din poziția sa sintactică, folosind proprietatea name. Acest lucru face posibilă identificarea funcției atunci când se inspectează valoarea acesteia sau se raportează o eroare.

Verificați acest lucru folosind anonymousArrowFunc:

console.log(anonymousArrowFunc.name)// output: "anonymousArrowFunc"

Dar fiți conștienți că această proprietate name dedusă există numai atunci când funcția anonimă este atribuită unei variabile, ca în exemplele de mai sus. Dacă folosiți o funcție anonimă ca un callback, de exemplu, pierdeți această caracteristică utilă. Acest lucru este exemplificat în demonstrația de mai jos, unde funcția anonimă din interiorul metodei .setInterval() nu se poate prevala de proprietatea name:

let counter = 5let countDown = setInterval(() => { console.log(counter) counter-- if (counter === 0) { console.log("I have no name!!") clearInterval(countDown) }}, 1000)

Și asta nu este tot. Această proprietate name dedusă încă nu funcționează ca un identificator propriu-zis pe care îl puteți utiliza pentru a vă referi la funcția din interiorul acesteia – cum ar fi pentru recursivitate, evenimente de dezlegare, etc.

Anonimatul intrinsec al funcțiilor săgeată l-a determinat pe Kyle Simpson să își exprime punctul de vedere asupra funcțiilor săgeată după cum urmează:

Din moment ce nu cred că funcțiile anonime sunt o idee bună pentru a fi utilizate frecvent în programele dumneavoastră, nu sunt un fan al utilizării formei de funcție săgeată =>. – You Don’t Know JS

Cum tratează funcțiile săgeată cuvântul cheie this

Cel mai important lucru de reținut despre funcțiile săgeată este modul în care acestea tratează cuvântul cheie this. În special, cuvântul cheie this în interiorul unei funcții săgeată nu primește ricoșeu.

Pentru a ilustra ce înseamnă acest lucru, urmăriți demonstrația de mai jos:

Vezi Pen
JS this in arrow functions by SitePoint (@SitePoint)
pe CodePen.

Iată un buton. Apăsarea butonului declanșează un contor invers de la 5 la 1. Numerele sunt afișate pe butonul însuși:

<button class="start-btn">Start Counter</button>...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) })

Observați cum gestionarul de evenimente din interiorul metodei .addEventListener() este o expresie de funcție anonimă obișnuită, nu o funcție săgeată. De ce? Dacă înregistrați this în interiorul funcției, veți vedea că aceasta face referire la elementul buton la care a fost atașat ascultătorul, ceea ce este exact ceea ce se așteaptă și ceea ce este necesar pentru ca programul să funcționeze așa cum a fost planificat:

startBtn.addEventListener('click', function() { console.log(this) ...})

Iată cum arată în consola de instrumente pentru dezvoltatori Firefox:

Cu toate acestea, încercați să înlocuiți funcția obișnuită cu o funcție săgeată, ca aceasta:

startBtn.addEventListener('click', () => { console.log(this) ...})

Acum, this nu mai face referire la buton. În schimb, face referire la obiectul Window:

Acest lucru înseamnă că, dacă doriți să folosiți this pentru a adăuga o clasă la buton după ce se face clic pe el, de exemplu, codul dvs. nu va funcționa:

// change button's border's appearancethis.classList.add('counting')

Iată mesajul de eroare din consolă:

Acest lucru se întâmplă pentru că, atunci când folosiți o funcție săgeată, valoarea cuvântului cheie this nu se reia, ci este moștenită din domeniul de aplicare al părintelui (acest lucru se numește lexical scoping). În acest caz particular, funcția săgeată în cauză este trecută ca argument la metoda startBtn.addEventListener(), care se află în domeniul global. În consecință, this din interiorul gestionarului funcției de săgeată este, de asemenea, legat de domeniul global – și anume, de obiectul Window.

Prin urmare, dacă doriți ca this să facă referire la butonul de pornire din program, abordarea corectă este să folosiți o funcție obișnuită, nu o funcție săgeată.

Următorul lucru care trebuie observat în demonstrația de mai sus este codul din interiorul metodei .setInterval(). Și aici veți găsi o funcție anonimă, dar de data aceasta este o funcție săgeată. De ce?

Observați care ar fi fost valoarea lui this dacă ați fi folosit o funcție obișnuită:

const timer = setInterval(function() { console.log(this) ...}, 1000)

Ar fi elementul button? Nici vorbă de așa ceva. Ar fi obiectul Window!

De fapt, contextul s-a schimbat, deoarece this se află acum în interiorul unei funcții nelegate sau globale care este transmisă ca argument pentru .setInterval(). Prin urmare, valoarea cuvântului cheie this s-a schimbat, de asemenea, deoarece acum este legat de domeniul global. Un hack obișnuit în această situație a fost acela de a include o altă variabilă pentru a stoca valoarea cuvântului cheie this, astfel încât să se refere în continuare la elementul așteptat – în acest caz, elementul button:

const that = thisconst timer = setInterval(function() { console.log(that) ...}, 1000)

Puteți utiliza și .bind() pentru a rezolva problema:

const timer = setInterval(function() { console.log(this) ...}.bind(this), 1000)

Cu funcțiile săgeată, problema dispare cu totul. Iată care este valoarea lui this atunci când folosiți o funcție săgeată:

const timer = setInterval( () => { console.log(this) ...}, 1000)

De data aceasta, consola înregistrează butonul, ceea ce este exact ceea ce este necesar. De fapt, programul va modifica textul butonului, așa că are nevoie de this pentru a se referi la elementul button:

const timer = setInterval( () => { console.log(this) // the button's text displays the timer value this.textContent = counter}, 1000)

Funcțiile săgeată nu au propriul context this. Ele moștenesc valoarea this de la părinte și, datorită acestei caracteristici, reprezintă o alegere excelentă în situații precum cea de mai sus.

Funcțiile săgeată nu sunt întotdeauna instrumentul potrivit pentru treabă

Funcțiile săgeată nu sunt doar un nou mod fantezist de a scrie funcții în JavaScript. Ele au propriile lor ciudățenii și limitări, ceea ce înseamnă că există cazuri în care nu doriți să folosiți o funcție săgeată. Manipulatorul de clic din demonstrația anterioară este un exemplu în acest sens, dar nu este singurul. Să examinăm alte câteva.

Funcțiile săgeată ca metode de obiect

Funcțiile săgeată nu funcționează bine ca metode pe obiecte. Iată un exemplu. Luați în considerare acest obiect netflixSeries, care are câteva proprietăți și câteva metode. Apelarea console.log(netflixSeries.getLikes()) ar trebui să tipărească un mesaj cu numărul curent de like-uri, iar apelarea console.log(netflixSeries.addLike()) ar trebui să mărească numărul de like-uri cu unu și apoi să tipărească noua valoare cu un mesaj de mulțumire pe consolă:

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` } }

În schimb, apelarea metodei .getLikes() returnează „nedefinit are NaN like-uri”, iar apelarea metodei .addLike() returnează „Mulțumim că vă place nedefinit, care acum are NaN like-uri”. Așadar, this.title și this.likes nu reușesc să facă referire la proprietățile obiectului title și, respectiv, likes.

Încă o dată, problema este legată de sfera lexicală a funcțiilor săgeată. this din interiorul metodei obiectului face referire la domeniul de aplicare al părintelui, care în acest caz este obiectul Window, nu părintele însuși – adică nu obiectul netflixSeries.

Soluția, bineînțeles, este de a utiliza o funcție obișnuită:

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 likesThank you for liking After Life, which now has 6 likes

Funcții arrow cu biblioteci terțe

O altă problemă de care trebuie să fim conștienți este că bibliotecile terțe vor lega adesea apelurile de metode astfel încât valoarea this să indice ceva util.

De exemplu, în interiorul unui gestionar de evenimente jQuery, this vă va oferi acces la elementul DOM la care a fost legat gestionarul:

$('body').on('click', function() { console.log(this)})// <body>

Dar dacă folosim o funcție săgeată – care, după cum am văzut, nu are propriul context this – obținem rezultate neașteptate:

$('body').on('click', () =>{ console.log(this)})// Window

Iată un alt exemplu care folosește Vue.js:

new Vue({ el: app, data: { message: 'Hello, World!' }, created: function() { console.log(this.message); }})// Hello, World!

În interiorul cârligului created, this este legat de instanța Vue, astfel încât este afișat mesajul „Hello, World!”.

Dacă folosim o funcție săgeată, totuși, this va indica domeniul părinte, care nu are o proprietate message:

new Vue({ el: app, data: { message: 'Hello, World!' }, created: function() { console.log(this.message); }})// undefined

Funcțiile săgeată nu au argumente obiect

Câteodată, este posibil să aveți nevoie să creați o funcție cu un număr nedefinit de parametri. De exemplu, să spunem că doriți să creați o funcție care să listeze serialele dvs. preferate de pe Netflix ordonate în funcție de preferințe. Cu toate acestea, nu știți încă câte seriale veți include. JavaScript pune la dispoziție obiectul „arguments”, un obiect asemănător unui tablou (nu un tablou complet, însă), care stochează valorile care sunt transmise funcției atunci când aceasta este apelată.

Încercați să implementați această funcționalitate folosind o funcție săgeată:

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')) 

Când apelați funcția, veți primi următorul mesaj de eroare: Uncaught ReferenceError: arguments is not defined. Acest lucru înseamnă că obiectul arguments nu este disponibil în interiorul funcțiilor săgeată. De fapt, înlocuirea funcției săgeată cu o funcție obișnuită rezolvă problema:

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: 

Deci, dacă aveți nevoie de obiectul arguments, nu puteți folosi funcții săgeată.

Dar ce se întâmplă dacă doriți cu adevărat să folosiți o funcție săgeată pentru a reproduce aceeași funcționalitate? Un lucru pe care îl puteți face este să folosiți parametrii de rest ES6 (...). Iată cum ați putea rescrie funcția dvs:

const listYourFavNetflixSeries = (...seriesList) => { return seriesList.map( (series, i) => { return `${series} is my #${i +1} favorite Netflix series` } ) }

Concluzie

Prin utilizarea funcțiilor săgeată, puteți să scrieți one-liners concisi cu return implicit și să uitați în sfârșit de hacks-urile din vechime pentru a rezolva legarea cuvântului cheie this în JavaScript. De asemenea, funcțiile săgeată funcționează foarte bine cu metode de matrice precum .map(), .sort(), .forEach(), .filter() și .reduce(). Dar nu uitați, funcțiile săgeată nu sunt un înlocuitor pentru funcțiile JS obișnuite și nu pot fi folosite pentru orice.

Dacă aveți întrebări despre funcțiile săgeată sau dacă aveți nevoie de ajutor pentru a le obține exact cum trebuie, vă recomand să treceți pe forumurile prietenoase de la SitePoint, unde există o mulțime de programatori pricepuți gata să vă ajute.

.

Lasă un răspuns

Adresa ta de email nu va fi publicată.