Guia completa de funcions fletxa i this en JavaScript

  • Les funcions fletxa aporten una sintaxi concisa i un this lèxic que hereta el context on es defineixen, cosa que simplifica enormement callbacks i codi asíncron.
  • Les funcions tradicionals mantenen un this dinàmic controlable amb call, apply i bind, i són les adequades per a constructors, mètodes dobjecte i patrons clàssics de POO.
  • Combinar correctament funcions fletxa amb ES6 (let, const, destructuring, spread i mètodes d'array) és clau per escriure codi modern, immutable i expressiu a JavaScript i React.
  • Conèixer quan evitar arrow functions (per exemple, com a mètodes d'objecte o constructors) és tan important com saber-les aprofitar en mòduls, manejadors d'esdeveniments i lògica funcional.

funcions fletxa i this en JavaScript

Si portes un temps programant en JavaScript segur que has sentit mil vegades parlar de les funcions fletxa i del famós comportament especial de aquest. I també és força probable que més d'una vegada t'hagi explotat el cap intentant entendre per què aquest val una cosa en una funció normal i una altra totalment diferent en una arrow function.

Dominar aquests dos conceptes no és només qüestió de teoria: és clau per escriure codi modern en JavaScript i React, entendre com funcionen els manejadors d'esdeveniments, com es comporta el context dins de classes, com es resolen callbacks asíncrons a Node.js i per què certs patrons són considerats bones pràctiques avui dia.

Primer de tot: repàs ràpid a funcions en JavaScript

A JavaScript pots declarar funcions de diverses formes, i cada forma té implicacions en com es comporta aquest, en el abast i en el moment en què la funció està disponible al codi.

Declaració de funció (function declaration)

Una declaració clàssica de funció utilitza la paraula clau function i un nom. Aquest tipus de funcions s'«eleven» (elevació) al principi de l'àmbit on es declaren, cosa que permet trucar a la funció fins i tot abans de la seva definició a l'arxiu.

En una funció declarada de forma tradicional, el valor de aquest es determina segons com s'invoca la funció: si es diu com a mètode d'un objecte, si es fa servir amb nou, si està en mode estricte, si es passa a cridar, aplicar o s'uneixen, etc. Aquesta flexibilitat és potent, però també és la font de moltíssims errors en codi complex.

Expressió de funció (function expression)

També pots assignar una funció anònima o nomenada a una variable. En aquest cas, la funció no s'eleva com a tal, només s'eleva la declaració de la variable (amb var), però no el seu valor. Amb deixar y const ni tan sols pots fer-la servir abans de la seva línia de definició per la temporal dead zone.

Aquestes funcions comparteixen el mateix model de this dinàmic que les declaracions clàssiques: el seu valor depèn sempre del context d'invocació, per la qual cosa segueixen sent sensibles a com les passes com a callback o com les fas servir dins de classes i objectes.

Com habilitar JavaScript en un telèfon Android
Article relacionat:
Com habilitar JavaScript en un telèfon Android

Què són realment les funcions fletxa?

Les funcions fletxa es van introduir amb ECMAScript 2015 (ES6) com una forma més còmoda, compacta i expressiva de declarar funcions, especialment per a callbacks i programació funcional amb mètodes com mapa, Filtrar o Reduir.

La sintaxi bàsica consisteix en una llista de paràmetres, seguida de l'operador => ia continuació una expressió o un bloc de codi. Aquesta forma compacta fa que el codi sigui més llegible a molts escenaris on abans havies d'escriure funcions anònimes llargues i repetitives.

Sintaxi bàsica de funcions fletxa

Les funcions fletxa admeten diverses variants de sintaxi, totes basades en les mateixes regles però amb dreceres per a casos simples. Entendre cada forma t'ajuda a escriure codi més net ia reconèixer ràpidament què fa cada callback.

  • Sense paràmetres: s'usen parèntesis buits abans de la fletxa i normalment una expressió després.
    const getMessage = () => "Hola";
  • Un sol paràmetre: pots ometre els parèntesis al voltant del nom del paràmetre, cosa que fa el codi més concís.
    const square = x => x * x;
  • Diversos paràmetres: sempre has d'usar parèntesis que els emboliquin, separats per comes.
    const sum = (a, b) => a + b;
  • Cos d´una sola expressió: si no poses claus, l'expressió es torna de forma implicat sense necessitat d'escriure return.
  • Cos de diverses línies: si utilitzes claus, has d'escriure return explícitament per tornar un valor, igual que en una funció normal.

Una font habitual d'errors és oblidar el return quan passes una versió d'una sola línia a un cos amb claus per afegir més lògica; de sobte la teva funció comença a tornar indefinit i no és evident per què.

Funcions fletxa i objectes literals

Si una funció fletxa d'una sola expressió torna un objecte literal, cal embolicar aquest objecte entre parèntesis perquè el motor no el confongui amb linici del bloc de la funció. És un petit parany de sintaxi que mossega molta gent.

En cas contrari, l'intèrpret interpreta les claus com a bloc buit i la teva funció no tornarà l'objecte, sinó indefinit, cosa que provoca errors difícils de rastrejar quan intentes accedir a propietats que mai es van tornar.

Les funcions fletxa i el comportament de this

El punt veritablement diferencial de les arrow functions no és la sintaxi, sinó la seva model de this. Mentre que les funcions tradicionals tenen un this dinàmic que depèn de com es diguin, les funcions fletxa tenen un this lèxic, és a dir, capturen el this de l'àmbit en què es creen i ho mantenen inalterat.

Això vol dir que dins d'una funció fletxa, aquest no es pot canviar amb cridar, aplicar ni s'uneixen, i tampoc depèn de si la fas servir com callback en un manejador d'esdeveniments o de si la passes a una altra funció. Sempre serà el mateix valor que en tenia fora en el moment de la seva definició.

Com funciona this en funcions normals?

En una funció declarada amb function, el valor de aquest depèn del context:

  • Si truques a la funció com un mètode d'un objecte, aquest apunta a aquest objecte.
  • Si la invoques sense context en mode no estricte, aquest referència al objecte global (window al navegador, global a Node.js).
  • si fas servir nou, aquest passa a apuntar a la instància recent creada.
  • Si apliques cridar, aplicar o s'uneixen, pots forçar el valor de aquest manualment a fi que prefereixis.

En mode estricte, si crides a una funció normal sense un objecte com a receptor i sense fer servir cridar o similars, aquest serà indefinit. Per això en molts exemples moderns veus funcions que comencen amb 'use strict' per evitar errors silenciosos i forçar un comportament més consistent.

Com funciona this en funcions fletxa?

En el cas de les arrow functions, aquest no es recalcula en invocar-les; es pren directament del àmbit on es van definir. És a dir, es comporten de manera semblant a com es capturen les variables per tancament (tancament): miren cap a fora i recorden aquest valor per sempre.

Això té diverses conseqüències molt importants per al teu dia a dia:

  • No pots fer servir una arrow function com constructor amb nou, perquè no tenen el seu propi aquest ni el prototip apropiat.
  • Trucar a una arrow function amb cridar o aplicar no canviarà el valor de aquest, encara que sí que podràs passar la resta d'arguments de forma normal.
  • En mètodes d'objectes creats com a funcions fletxa, aquest no apuntarà a l'objecte, sinó al entorn superior (per exemple, window o el mòdul), cosa que gairebé sempre és un bug.

La lectura clau és que les funcions fletxa són ideals quan vols que el this intern sigui el mateix que l'extern, però són mala idea quan necessites un mètode real d'objecte o quan pretens instanciar alguna cosa amb nou.

Funcions fletxa, classes i React

funcions fletxa i this en JavaScript

En el món de Reaccionar i de les classes de JavaScript en general, el maneig de aquest és un tema sensible. Els components de classe heretats de Reaccionar.Component utilitzen mètodes en què aquest ha d'apuntar de forma consistent a la instància del component per poder accedir a this.props y aquest.estat.

Amb mètodes definits usant la sintaxi clàssica de classe, el valor de aquest es pot «perdre» en passar un mètode com a callback d'esdeveniment, per exemple a a Feu clic. Per això en molts tutorials antics veuràs patrons com this.metodo.bind(this) al constructor o directament al JSX, una cosa que avui dia es considera bastant verbós.

Patrons típics de binding de this a React

En un component de classe, hi havia històricament quatre formes principals d'assegurar-te que aquest apunta sempre al component quan es truca des d'un manejador d'esdeveniments:

  • Utilitza s'uneixen en el mètode fer, el que assegura el context però crea una nova funció a cada render.
  • fer el bind al constructor per evitar recrear la funció una vegada i una altra, mantenint una única referència.
  • Definir el manejador com propietat de classe amb arrow function, aprofitant que les funcions fletxa capturen el aquest de la instància.
  • Utilitzar arrow function inline al JSX, passant directament una funció fletxa a l'atribut a Feu clic o equivalent.

A la pràctica, l'opció de declarar el manejador com propietat de classe usant una funció fletxa es va tornar el patró més còmode: escrius menys codi, evites haver de fer s'uneixen manual i aquest sempre apunta al component sense sorpreses.

Funcions fletxa en components de funció

Amb l'arribada dels components funcionals i els hooks, React es recolza moltíssim més en funcions fletxa. La majoria de components moderns s'implementen com const MiComponent = () => { … }, el que fa natural i consistent l'ús d'arrow functions a tot l'arbre.

Dins d'aquests components, és habitual combinar funcions fletxa amb altres característiques d'ES6 com desestructuració, template literals, operadors ternaris, difondre i mètodes d'array com mapa, Filtrar y Reduir, construint així una sintaxi molt expressiva però que exigeix ​​manejar bé el context i la immutabilitat.

this en l'àmbit global, objectes i funcions soltes

Per entendre per què les arrow functions canvien tant el joc, convé repassar com es comporta aquest en diferents contextos de JavaScript nadiu, tant a navegador com a Node.js.

En el àmbit global del navegador, fora de qualsevol funció o mòdul, aquest apunta a l'objecte finestra. Això vol dir que qualsevol variable declarada amb var a nivell superior penja daquest objecte global i és accessible tant per nom com per window.nom, cosa que en aplicacions complexes es considera mala pràctica per la contaminació global.

this dins d'objectes literals

Quan defineixes un objecte literal amb mètodes tradicionals, cada mètode rep un aquest que referència al propi objecte en invocar-lo com obj.metodo(). Aquesta és la forma estàndard d'accedir a propietats germanes amb this.propietat sense necessitat de variables intermèdies.

El problema apareix quan substitueixes aquests mètodes per funcions fletxa. En no tenir this propi, l'arrow function capturarà el this extern, que probablement sigui el global o el del mòdul. El resultat és que dins del mètode no podràs accedir a les propietats de l'objecte usant aquest, trencant per complet la intenció original del disseny.

Ús avançat de Sublim Text per a edició ràpida
Article relacionat:
Ús avançat de Sublim Text per a edició ràpida

this dins de funcions soltes i mode estricte

En funcions tradicionals anomenades «a pèl», sense objecte receptor, el valor de aquest depèn de si el codi és a manera estricta o no. En no estricte, torna a caure al objecte global, mentre que en estricte es torna explícitament indefinit, per evitar accions perilloses sobre l'entorn global.

Aquest comportament canvia dràsticament quan encapsules el teu codi en mòduls d'ES, bundlers moderns o IIFEs, on el global pot ser diferent o no estar lligat a aquest de la forma habitual, sobretot a NODE.JS, on l'àmbit principal de l'arxiu és el del mòdul i no el global.

call, apply, bind i la seva relació amb this

JavaScript ofereix tres mètodes molt usats sobre les funcions clàssiques per gestionar el context: cridar, aplicar y s'uneixen. Tots ells permeten controlar explícitament quin valor pren aquest quan la funció sexecuta.

Aquests mètodes funcionen únicament amb funcions declarades amb function (o equivalents), ja que les arrow functions ignoren qualsevol intent de canviar el seu this. Aquí hi ha el motiu pel qual tots tres són tan útils en treballar amb APIs heretades, classes antigues o patrons on es vol reutilitzar una mateixa funció sobre diferents objectes.

call i apply: invocar amb this explícit

tant cridar com a aplicar executen la funció immediatament, l'única cosa que canvia és com passes els arguments. En tots dos casos, el primer paràmetre que els passes és l'objecte que s'ha de convertir en aquest dins de la funció.

Amb cridar els arguments posteriors s'indiquen un per un, mentre que amb aplicar es passen com un formació. Això encaixa molt bé amb situacions on ja tens la llista d'arguments com a col·lecció i vols injectar-la tal com a la trucada.

bind: crear una nova funció amb this fixat

el mètode s'uneixen no executa la funció en el moment, sinó que en genera una nova funció amb el this permanentment associat a fi que li indiquis. És com crear una versió especialitzada de l'original que sempre es comportarà amb aquest context.

Aquest patró és molt habitual per passar mètodes d'objectes a APIs que després els anomenaran de forma diferida, per exemple com a callbacks d'esdeveniments, sense que el context original es perdi. Abans de la popularització de les arrow functions, s'uneixen era una de les maneres més netes de garantir que un mètode de classe seguia apuntant a la instància correcta.

Per què les funcions fletxa són ideals per callbacks?

Una de les raons per les quals les arrow functions han conquerit l'ecosistema JavaScript és la seva idoneïtat per callbacks asíncrons i programació funcional. Quan treballes amb APIs com setTimeout, setInterval, promeses o amb el bucle d'esdeveniments de Node.js, necessites sovint accedir al context exterior sense que es distorsioni.

Abans d'ES6, es recorria a trucs com guardar const self = this o const that = this abans d'entrar al callback, per continuar podent referir-se a l'objecte correcte des de dins de la funció. Les funcions fletxa fan això innecessari en capturar automàticament el this extern i conservar-ho.

Arrow functions i el bucle d'esdeveniments a Node.js

En NODE.JS, on la major part del codi interessant gira al voltant de I/O asíncrona (fitxers, xarxa, timers), la combinació d'arrow functions amb el model de event loop i cues de callbacks simplifica molt la vida. Cada vegada que passes una funció com a manejador a fs.readFile, a un servidor HTTP oa qualsevol operació de libuv, les arrow functions ajuden a mantenir la referència a variables contigües i al context de mòdul sense sorpreses.

A més, a les cues de microtasques com les de promeses o process.nextTick, és molt habitual escriure callbacks curts tipus llavors o Agafar amb sintaxi fletxa, aprofitant tant el return implícit com this lèxic, el que fa el codi més declaratiu i expressiu.

Quan NO utilitzar funcions fletxa?

Encara que les arrow functions semblin la solució màgica a tots els mals, hi ha escenaris en què el seu ús és directament contraproduent i fins i tot genera errors difícils de detectar.

Un d'ells, potser el més important, són els mètodes d'objectes o classes que necessiten el vostre this. Un altre és quan vols crear constructors o utilitzar patrons orientats a objectes recolzats en prototips i herència, on l'absència de prototip i de context propi fa que les funcions fletxa simplement no encaixin.

Evita arrow functions com a mètodes d'objecte

Si definiu un mètode d'un objecte amb una funció fletxa, el this intern no farà referència a l'objecte, sinó al context en què es va definir aquest objecte. Això trenca les expectatives de qualsevol que llegeixi el codi i pretengui usar this.propietat dins del mètode.

La forma recomanada d'escriure mètodes que depenen de aquest és seguir utilitzant la sintaxi clàssica en objectes literals o declarar aquests mètodes de forma estàndard a classes, reservant les functions fletxa per a callbacks interns que necessitin capturar aquest des d'un mètode superior.

No utilitzis arrow functions com a constructors

Les arrow functions no tenen [[Construct]], el mecanisme intern que permet a una funció ser invocada amb nou. Per tant, intentar utilitzar-les com a constructors generarà un TypeError. Tampoc no tenen el seu propi prototip, de manera que no podeu penjar mètodes compartits per a instàncies.

Si el teu disseny exigeix ​​instàncies reutilitzables amb propietats i mètodes comuns, has d'optar per classes o per funcions constructores tradicionals, deixant les arrow functions per a lògica sense estat o utilitats purament funcionals.

Relació amb altres característiques modernes de JavaScript

Les funcions fletxa poques vegades s'usen aïllades; gairebé sempre van de la mà d'altres característiques de ECMAScript modern que també afecten com organitzes i entens el codi. Algunes de les més relacionades són deixar y const, l' immutabilitat, el desestructuració i la sintaxi spread.

Saber combinar-les correctament permet escriure funcions fletxa molt concises que transformen dades amb mètodes d'array com mapa, Filtrar y Reduir, mantenint sempre l'estat original intacte, una cosa crucial en entorns com React on el patró d'immutabilitat és la norma.

Edició avançada de text i automatització amb Notepad++
Article relacionat:
Edició avançada de text i automatització amb Notepad++

let, const i immutabilitat

L'arribada de deixar y const va permetre treballar amb àmbits de bloc i amb referències que no es reassignen. En combinació amb arrow functions, el que és habitual avui és declarar funcions com const miFuncion = () => {…}, garantint que la variable que conté la funció no se sobreescriurà.

En estructures de dades com ara arrays i objectes, la immutabilitat es fomenta mitjançant l'ús de Object.freeze quan vols evitar canvis en profunditat, i mitjançant operacions amb spread i mètodes purs quan necessites crear còpies modificades en lloc d'alterar l'original, especialment important quan el renderitzat o l'estat depenen de detectar canvis.

Template literals, ternaris i short-circuit

En callbacks de renderitzat, per exemple a JSX o en construir cadenes HTML, és gairebé inevitable barrejar arrow functions amb template literals (les cometes invertides) i operadors com el ternari o la avaluació per curtcircuit. Aquests operadors permeten escriure condicions inline molt expressives que encaixen molt bé amb lestil declaratiu de les funcions fletxa.

Dins literals de plantilla, les expressions ${…} es resolen amb el mateix abast lèxic que domina el this de les arrow functions, per la qual cosa és fonamental tenir clar quines variables i quin context s'estan fent servir en cada interpolació per evitar comportaments ambigus.

Funcions fletxa en iteracions i col·leccions

Un dels camps on les arrow functions han marcat més diferència és en el tractament de arrays i col·leccions. Passar callbacks curts a mètodes com mapa, Filtrar, trobar o Reduir es torna enormement més llegible amb la sintaxi fletxa.

Per exemple, filtrar entrades d'un llistat segons una categoria o transformar resultats d'una API en estructures de dades internes es fa molt més declaratiu i combinat amb desestructuració és fàcil extreure només les propietats que necessites dins dels paràmetres de la funció.

map, filter, redueix i companyia

La combinació típica sol ser una cosa així com llista.filter(item => condició).map(item => transformació), encadenant diverses funcions fletxa en mètodes d'array. Cadascuna hereta el this del context on es defineix, encara que en la majoria d'aquests casos ni tan sols es fa servir this, sinó paràmetres explícits, reforçant l'estil funcional.

En Reduir, on maneges un acumulador que va passant d'iteració en iteració, les arrow functions permeten expressar l'operació reductora de forma molt concisa, encara que convé no abusar d'expressions massa denses si vols que el codi segueixi sent comprensible per a altres desenvolupadors.

Importacions, exportacions i mòduls

A l'ecosistema modern de JavaScript, gairebé tot el codi s'organitza a mòduls, ja sigui usant la sintaxi nativa importació-exportació o utilitzant sistemes de bundling que compilen aquest format. En aquest context, les funcions fletxa encaixen molt bé com a unitats de funcionalitat reutilitzables.

És habitual veure utilitats exportades com const miUtil = (…) => {…} i després importades en diferents fitxers. En estar cada mòdul en el seu propi àmbit, el this lèxic de les arrow functions tendeix a ser el del mòdul, cosa que en la majoria d'utilitats pures no és ni tan sols rellevant, ja que no necessiten context.

Bones pràctiques amb mòduls i scripts externs

Quan serveixes JavaScript en una pàgina HTML, és preferible carregar-lo mitjançant arxius externs i no incrustar grans blocs inline. A més, fer servir l'atribut type=»module» a l'etiqueta script habilita la sintaxi moderna de importació-exportació i deixa clar que el fitxer s'executa en mode estricte per defecte.

Quant al rendiment, col·locar els scripts de mòdul al cap amb ajornar permet que el navegador descarregui i executi el codi sense bloquejar el renderitzat de l'HTML, mantenint una bona experiència d'usuari. En tot aquest esquema, les funcions fletxa són simplement la forma més natural de definir petites peces de lògica reutilitzable.

Inserció i manipulació del DOM amb JavaScript modern

Encara que el focus d'aquest tema són les funcions fletxa i this, és impossible ignorar el paper de JavaScript a la manipulació del DOM. Mètodes com getElementById, querySelector, setAttribute o la modificació de estil segueixen sent bàsics, però avui es combinen habitualment amb callbacks fletxa per reaccionar a esdeveniments i actualitzar la interfície.

treballar amb oients d'esdeveniments usant element.addEventListener('click', e => {...}) és la forma estàndard dassociar lògica a interaccions dusuari. En aquests manejadors, aquest dins de l'arrow function no serà l'element DOM, sinó el context extern, per tant, si necessites el node hauràs d'usar el propi paràmetre d'esdeveniment e.currentTarget o e.target, al lloc de confiar en this com en els vells attributs inline.

JavaScript síncron, asíncron i callbacks

Un altre dels grans escenaris on les arrow functions marquen la diferència és a la programació asíncrona. JavaScript és d'un sol fil i, per no bloquejar la interfície, delega les operacions lentes en API del navegador o de Node que després truquen als teus callbacks quan acaben.

Passar funcions fletxa com callbacks a setTimeout, promet, peticions HTTP o accessos a disc fa que el codi sigui més compacte i que el context es mantingui sense necessitat de trucs addicionals. En el pas de callbacks niats cap a promeses i posteriorment cap a asíncron/espera, les arrow functions han estat una peça imprescindible per donar llegibilitat al flux de dades.

Windows com thin client
Article relacionat:
Windows com thin client: configurar Remote Desktop i polítiques de sessió

En conjunt, entendre com les funcions fletxa capturen this lèxicament, en quins casos són la teva millor aliada i en quins convé seguir confiant en les funcions tradicionals, juntament amb saber combinar-les amb mòduls, mètodes de matriu, immutabilitat, classes i el bucle d'esdeveniments, és el que et permet escriure JavaScript i React realment sòlids; al final, es tracta d'escollir la forma de funció adequada per a cada context, aprofitar els seus avantatges sintàctics sense caure en els casos límit on el seu comportament de this et podria jugar una mala passada. Comparteix aquesta guia de programació i les funcions fletxa de JavaScript.