Hvis du har programmeret i JavaScript i et stykke tid, har du helt sikkert hørt om... pilfunktioner og den berømte særlige opførsel af denneOg det er også ret sandsynligt, at dit hoved er eksploderet mere end én gang i forsøget på at forstå hvorfor. denne Én ting gælder for en normal funktion og noget helt andet for en pilefunktion.
At mestre disse to koncepter er ikke kun et spørgsmål om teori: det er nøglen til at skrive. Moderne kode i JavaScript og ReactForståelse af, hvordan eventhandlere fungerer, hvordan kontekst opfører sig i klasser, hvordan asynkrone callbacks løses i Node.js, og hvorfor bestemte mønstre betragtes som god praksis i dag.
Først og fremmest: en hurtig gennemgang af funktioner i JavaScript
I JavaScript kan du deklarere funktioner på flere måder, og hver måde har konsekvenser for, hvordan de opfører sig. denne, I rækkevidde og i øjeblikket er funktionen tilgængelig i koden.
Funktionsdeklaration
En klassisk funktionsdeklaration bruger nøgleordet funktion og et navn. Disse typer funktioner er "forhøjede" (hejsning) i begyndelsen af det omfang, hvor de er deklareret, hvilket gør det muligt at kalde funktionen selv før dens definition i filen.
I en funktion, der er deklareret på en traditionel måde, er værdien af denne bestemmes i henhold til hvordan Funktionen kaldes: hvis den kaldes som en metode til et objekt, hvis den bruges med ny, hvis den er i streng tilstand, hvis den skifter til ringe, ansøge o bindeosv. Denne fleksibilitet er kraftfuld, men den er også kilden til mange fejl i kompleks kode.
Funktionsudtryk
Du kan også tildele en anonym eller navngiven rolle til en variabelI så fald hæves selve funktionen ikke, kun variabeldeklarationen hæves (med var), men ikke dens værdi. Med lad y const Du kan ikke engang bruge den før dens definitionslinje på grund af tidsmæssig død zone.
Disse funktioner deler den samme model af denne dynamik I modsætning til klassiske deklarationer: deres værdi afhænger altid af konteksten for kaldelsen, så de forbliver følsomme over for, hvordan du sender dem som et tilbagekald, eller hvordan du bruger dem i klasser og objekter.
Hvad er pilfunktioner præcist?
Pilfunktionerne blev introduceret med ECMAScript 2015 (ES6) som en mere bekvem, kompakt og udtryksfuld måde at deklarere funktioner på, især til callbacks og funktionel programmering med metoder som kort, filtrere o reducere.
Dens grundlæggende syntaks består af en liste over parametre efterfulgt af operatoren => og så en ekspression eller kodeblokDette kompakte format gør koden mere effektiv. læsbar i mange scenarier, hvor man tidligere skulle skrive lange og gentagne anonyme funktioner.
Grundlæggende syntaks for pilefunktioner
Pilfunktioner understøtter adskillige syntaksvariationer, alle baseret på de samme regler, men med genveje til enklere tilfælde. Forståelse af hver form hjælper dig med at skrive dem. renere kode Jeg kan hurtigt genkende, hvad hvert tilbagekald gør.
- Uden parametreTomme parenteser bruges før pilen og normalt et udtryk efter.
const getMessage = () => "Hola"; - En enkelt parameterDu kan udelade parenteserne omkring parameternavnet, hvilket gør koden mere præcis.
const square = x => x * x; - Flere parametreDu skal altid bruge parenteser, der omslutter dem, adskilt af kommaer.
const sum = (a, b) => a + b; - Brødteksten af et enkelt udtrykHvis du ikke bruger krøllede parenteser, returneres udtrykket på en bestemt måde. implicit uden at skulle skrive afkast.
- Flerlinjet kropHvis du bruger bøjler, skal du skrive afkast eksplicit for at returnere en værdi, ligesom i en normal funktion.
En almindelig kilde til fejl er at glemme afkast Når du går fra en version med én linje til en brødtekst med krøllede parenteser for at tilføje mere logik, begynder din funktion pludselig at vende tilbage. undefined Og det er ikke indlysende hvorfor.
Pilfunktioner og literale objekter
Hvis en pilfunktion med et enkelt udtryk returnerer en bogstaveligt objektDu skal sætte objektet i parenteser, så motoren ikke forveksler det med starten af funktionsblokken. Det er en lille syntaksfælde, der overrasker mange.
Ellers fortolker fortolkeren nøglerne som en tom blok, og din funktion returnerer ikke objektet, men undefinedDette forårsager svært sporbare fejl, når du forsøger at få adgang til egenskaber, der aldrig blev returneret.
Pilfunktioner og deres opførsel
Den virkelig differentierende faktor ved pilefunktioner er ikke syntaksen, men deres model af detteMens traditionelle funktioner har en denne dynamik Afhængigt af deres navne har pilfunktioner en dette leksikonDet vil sige, de fanger Dette er fra det område, hvor de er skabt og de beholder det uændret.
Det betyder, at inden for en pilefunktion, denne Det kan ikke ændres med ringe, ansøge ni bindeOg det afhænger ikke af, om du bruger det som et callback i en eventhandler eller sender det til en anden funktion. Det vil altid have den samme værdi, som det havde uden for det på tidspunktet for dets definition.
Hvordan fungerer dette i normale funktioner?
I en funktion deklareret med funktion, værdien af denne Det afhænger af konteksten:
- Hvis du kalder funktionen som en metode for et objekt, denne Peg på det objekt.
- Hvis du kalder den uden kontekst i ikke-strict tilstand, denne henvisning til globalt objekt (vindue i browseren, globalt i Node.js).
- Hvis du bruger ny, denne den peger så på nyoprettet instans.
- Hvis du ansøger ringe, ansøge o binde, kan du tvinge værdien af denne manuelt til det valgte objekt.
I streng tilstand, hvis du kalder en normal funktion uden et objekt som modtager og uden at bruge en ringe eller lignende, denne vil være undefinedDerfor ser man i mange moderne eksempler funktioner, der starter med 'use strict' for at undgå tavse fejl og fremtvinge mere ensartet adfærd.
Hvordan fungerer dette i pilefunktioner?
I tilfælde af pilfunktioner, denne Den genberegnes ikke, når den kaldes; den tages direkte fra område, hvor de blev defineretDet vil sige, at de opfører sig på samme måde som variabler indfanges ved lukning (lukning): De ser udad og husker den værdi for altid.
Dette har flere meget vigtige konsekvenser for din hverdag:
- Du kan ikke bruge en pilefunktion som f.eks. konstruktør med nyfordi de ikke har deres egne denne heller ikke den passende prototype.
- Kald af en pilfunktion med ringe o ansøge vil ikke ændre værdien af denneDu vil dog kunne gennemføre resten af argumenterne normalt.
- I metoder til objekter oprettet som pilefunktioner, denne Den vil ikke pege på objektet, men på øvre miljø (for eksempel vinduet eller modulet), hvilket næsten altid er en fejl.
Den vigtigste konklusion er, at pilefunktioner er ideelle, når du vil have Denne interne er den samme som den eksterne.Men de er en dårlig idé, når du har brug for en metode med reel objekt eller når du har til hensigt at instantiere noget med ny.
Pilfunktioner, klasser og React

I verden af Reagerer og af JavaScript-klasser generelt, håndteringen af denne Det er et følsomt emne. Klassekomponenterne arvet fra React.Component De bruger metoder, hvor denne skal konsekvent pege på komponentinstansen for at få adgang dette.rekvisitter y denne.stat.
Med metoder defineret ved hjælp af klassisk klassesyntaks, er værdien af denne Den kan gå "tabt", når en metode sendes som et event-tilbagekald, for eksempel til onclickDerfor ser du i mange ældre tutorials mønstre som this.metode.bind(dette) i konstruktøren eller direkte i JSX, noget der anses for at være ret ordrigt nu om dage.
Typiske bindingsmønstre i React
I en klassekomponent var der historisk set fire primære måder at sikre, at denne peger altid på komponenten, når den kaldes fra en eventhandler:
- Brug binde i metoden udbytte, hvilket sikrer kontekst, men opretter en ny funktion i hver gengivelse.
- Lav bind i konstruktøren for at undgå at genskabe funktionen igen og igen, ved at bevare en enkelt reference.
- Definer handleren som klasseegenskab med pilfunktion, udnytter det faktum, at pilfunktionerne indfanger denne af instansen.
- Brug pilfunktion indlejret i JSX, der sender en pilefunktion direkte til attributten onclick eller tilsvarende.
I praksis er muligheden for at erklære behandleren som klasseegenskab ved hjælp af en pilefunktion Det blev det mest bekvemme mønster: du skriver mindre kode, du undgår at skulle gøre binde manual og denne Sigt altid efter komponenten uden overraskelser.
Pilfunktioner i funktionskomponenter
Med ankomsten af funktionelle komponenter og krogeReact er langt mere afhængig af pilefunktioner. De fleste moderne komponenter implementeres som pile. const MinKomponent = () => { … }Dette gør brugen af pilefunktioner naturlig og ensartet i hele træet.
Inden for disse komponenter er det almindeligt at kombinere pilfunktioner med andre ES6-funktioner, såsom destrukturering, skabelon bogstaver, ternære operatorer, spredes og array-metoder som f.eks. kort, filtrere y reducere, og konstruerer dermed en meget udtryksfuld syntaks, men en der kræver god håndtering af kontekst og uforanderlighed.
Dette i den globale kontekst, individuelle objekter og funktioner
For at forstå, hvorfor pilefunktioner ændrer spillet så meget, er det værd at gennemgå, hvordan de opfører sig. denne i forskellige native JavaScript-kontekster, både i browseren og i Node.js.
I browserens globale omfang, uden for enhver funktion eller modul, denne peg på objektet vindueDet betyder, at enhver variabel, der erklæres med var På et højere niveau hænger den på det globale objekt og er tilgængelig både efter navn og efter vinduesnavnDette betragtes som dårlig praksis i komplekse applikationer på grund af global forurening.
dette inden for bogstavelige objekter
Når du definerer en bogstaveligt objekt Med traditionelle metoder modtager hver metode en denne der refererer til selve objektet, når det påkaldes som obj.metode()Dette er standardmetoden til at få adgang til søsterejendomme med denne.ejendom uden behov for mellemliggende variabler.
Problemet opstår, når man erstatter disse metoder med pilfunktionerDa vi ikke har denne egenFunktionen pil vil registrere den eksterne `this`, som sandsynligvis er den globale eller modulemæssige `this`. Som følge heraf vil du ikke kunne få adgang til objektets egenskaber i metoden ved hjælp af denne, hvilket fuldstændig bryder designets oprindelige intention.
Dette er inden for løse funktioner og streng tilstand
I traditionelle funktioner kaldet "unplugged", uden et modtagende objekt, er værdien af denne Det afhænger af, om koden er i streng tilstand Eller ej. Strengt taget falder det tilbage i globalt objekt, mens det strengt taget bliver eksplicit undefinedfor at undgå farlige handlinger på det globale miljø.
Denne adfærd ændrer sig drastisk, når du indkapsler din kode i ES-moduler, moderne bundlere eller IIFE'er, hvor den globale kode kan være forskellig eller ikke bundet til denne på sædvanlig måde, især i node.js, hvor arkivets hovedomfang er modul og ikke den globale.
kalde, anvende, binde og deres forhold til dette
JavaScript tilbyder tre udbredte metoder i forhold til klassiske funktioner til håndtering af kontekst: ringe, ansøge y bindeDe giver dig alle mulighed for eksplicit at kontrollere, hvilken værdi det tager. denne når funktionen udføres.
Disse metoder virker kun med funktioner deklareret med funktion (eller tilsvarende), da pilefunktioner ignorerer ethvert forsøg på at ændre deres `dette`. Derfor er alle tre så nyttige, når man arbejder med ældre API'er, gamle klasser eller mønstre, hvor man vil genbruge den samme funktion på forskellige objekter.
kald og anvend: påkald med eksplicit dette
så ringe som ansøge de udfører funktionen med det sammeDen eneste forskel er, hvordan du sender argumenterne. I begge tilfælde er den første parameter, du sender, det objekt, der skal konverteres. denne inden for funktionen.
med ringe De efterfølgende argumenter anføres et efter et, hvorimod med ansøge De udgiver sig for at være en matrixDette passer rigtig godt til situationer, hvor du allerede har listen over argumenter som en samling, og du ønsker at injicere den, som den er, i kaldet.
bind: opret en ny funktion med `this`-bindingen
Metoden binde Den udfører ikke funktionen i øjeblikket, men genererer i stedet en ny funktion med dette permanent forbundne til det objekt, du angiver. Det er som at oprette en "specialiseret" version af originalen, der altid vil opføre sig i den kontekst.
Dette mønster er meget almindeligt for at sende objektmetoder til API'er, der senere kalder dem, for eksempel som event-tilbagekald, uden at miste den oprindelige kontekst. Før populariseringen af pilefunktioner, binde Det var en af de reneste måder at sikre, at en klassemetode fortsatte med at pege på den korrekte instans.
Hvorfor er pilfunktioner ideelle til tilbagekald?
En af grundene til, at pilefunktioner har erobret JavaScript-økosystemet, er deres egnethed til asynkrone tilbagekald og funktionel programmering. Når man arbejder med API'er som f.eks. sætTimeout, sætInterval, Promises Eller med Node.js-eventloopet har du ofte brug for at få adgang til den eksterne kontekst uden at den bliver forvrænget.
Før ES6 blev tricks som at gemme brugt konstant selv = dette o konstant at = dette før tilbagekaldet indtastes, så det korrekte objekt stadig kan refereres indefra funktionen. Pilfunktioner gør dette unødvendigt ved automatisk at registrere Denne eksterne og bevare den.
Pilfunktioner og eventløkken i Node.js
En node.js, hvor det meste af den interessante kode drejer sig om Asynkron I/O (filer, netværk, timere), kombinationen af pilefunktioner med modellen af hændelsesløkke og tilbagekaldskøer Det forenkler livet meget. Hver gang du sender en funktion som et handle til fs.læsefilUanset om du sender en HTTP-server eller en anden libuv-operation, hjælper pilefunktioner med at holde referencer til sammenhængende variabler og modulkontekster fri for overraskelser.
Desuden, i mikrotask-køer som dem fra Promises o proces.nextTickDet er meget almindeligt at skrive korte callbacks som derefter o fangst med pilesyntaks, der udnytter begge dele implicit afkast som dette leksikon, hvilket gør koden mere deklarativ og udtryksfuld.
Hvornår bør du IKKE bruge pilefunktioner?
Selvom pilefunktioner kan virke som den magiske løsning på alle problemer, er der scenarier, hvor deres anvendelse er direkte kontraproduktivt og genererer endda fejl, der er vanskelige at opdage.
En af dem, måske den vigtigste, er metoder til objekter eller klasser, der har brug for deres eget `dette`En anden er, når du vil skabe bygherrer eller bruge objektorienterede mønstre understøttet af prototyper og arv, hvor fraværet af prototype og dens egen kontekst betyder, at pilefunktioner simpelthen ikke passer.
Undgå at bruge pilefunktioner som objektmetoder.
Hvis du definerer en metode til et objekt med en pilefunktion, Denne interne vil ikke referere til objektetmen snarere den kontekst, hvori objektet blev defineret. Dette bryder med forventningerne hos alle, der læser koden og har til hensigt at bruge denne.ejendom inden for metoden.
Den anbefalede måde at skrive metoder, der afhænger af denne Det handler om at fortsætte med at bruge den klassiske syntaks i literal-objekter eller at deklarere disse metoder på en standardmåde i klasser, og reservere pilefunktioner til interne callbacks, der skal fange denne fra en overlegen metode.
Brug ikke pilefunktioner som konstruktører
Pilfunktionerne mangler [[Konstruere]], den interne mekanisme, der tillader, at en funktion kan kaldes med nyDerfor vil forsøg på at bruge dem som konstruktører generere en TypeFejlDe har heller ikke deres egne. prototypeDerfor kan du ikke hænge delte metoder for instanser.
Hvis dit design kræver genanvendelige instanser med fælles egenskaber og metoder, bør du vælge clases eller ved traditionelle konstruktørfunktioner, hvor pilefunktioner efterlades til statsløs logik eller rent funktionelle værktøjer.
Forholdet til andre moderne funktioner i JavaScript
Pilfunktioner bruges sjældent isoleret; de går næsten altid hånd i hånd med andre funktioner i Moderne ECMAScript som også påvirker, hvordan du organiserer og forstår kode. Nogle af de mest relaterede er lad y const, uforanderlighed, The destrukturering og spread syntaks.
At vide, hvordan man kombinerer dem korrekt, giver dig mulighed for at skrive meget præcise pilefunktioner, der transformerer data med array-metoder som f.eks. kort, filtrere y reducerealtid at bevare den oprindelige tilstand intakt, noget afgørende i miljøer som React, hvor uforanderlighedsmønsteret er normen.
lad, konstant og uforanderlighed
Ankomsten af lad y const Det gjorde det muligt at arbejde med blokområder og med referencer, der ikke tildeles igen. I kombination med pilefunktioner er det almindelig praksis i dag at deklarere funktioner som const minFunktion = () => {…}, hvilket sikrer, at variablen, der indeholder funktionen, ikke overskrives.
I datastrukturer såsom arrays og objekter fremmes uforanderlighed gennem brug af Objektfrysning når du vil undgå dybe ændringer, og gennem spredningsoperationer og rene metoder, når du har brug for at oprette modificerede kopier I stedet for at ændre originalen, afhænger noget særligt vigtigt ved gengivelse eller tilstand af at detektere ændringer.
Skabelonliteraler, ternære værdier og kortslutning
Ved rendering af callbacks, for eksempel i JSX eller ved konstruktion af HTML-strenge, er det næsten uundgåeligt at blande pilefunktioner med skabelon bogstaver (backticks) og operatører som f.eks. ternær o kortslutningsevalueringDisse operatorer giver dig mulighed for at skrive meget udtryksfulde inline-betingelser, der passer rigtig godt til den deklarative stil i pilefunktioner.
Inden for skabelonliteraler, udtrykkene ${…} De løses med det samme leksikalske omfang, der dominerer `dette` i pilefunktioner, så det er vigtigt at være klar over, hvilke variabler og hvilken kontekst der bruges i hver interpolation for at undgå tvetydig adfærd.
Pilfunktioner i iterationer og samlinger
Et af de områder, hvor pilefunktioner har gjort den største forskel, er i behandlingen af arrays og samlingerVideregivelse af korte callbacks til metoder som kort, filtrere, find o reducere Det bliver langt mere læsbart med pilesyntaksen.
For eksempel bliver filtrering af poster i en liste baseret på en kategori eller omdannelse af API-resultater til interne datastrukturer meget mere deklarativt, og kombineret med destrukturering Det er nemt kun at udtrække de egenskaber, du har brug for, fra funktionens parametre.
kortlægge, filtrere, reducere og firma
Den typiske kombination er normalt noget i retning af dette: list.filter(item => condition).map(item => transformation)kæde flere pilefunktioner sammen med arraymetoder. Hver af dem arver `this` fra den kontekst, hvor den er defineret, selvom `this` i de fleste af disse tilfælde ikke engang bruges, men snarere eksplicitte parametre, hvilket forstærker den funktionelle stil.
En reducereI dette tilfælde, hvor du administrerer en akkumulator, der går fra iteration til iteration, giver pilefunktioner dig mulighed for at udtrykke reduktionsoperationen meget præcist, selvom det er tilrådeligt ikke at bruge for meget kompakte udtryk, hvis du ønsker, at koden skal forblive forståelig for andre udviklere.
Import, eksport og moduler
I det moderne JavaScript-økosystem er næsten al kode organiseret i modulerenten ved hjælp af native syntaks import Eksport eller ved at bruge bundlingssystemer, der kompilerer til det format. I denne sammenhæng passer pilfunktioner rigtig godt som genbrugelige funktionalitetsenheder.
Det er almindeligt at se forsyningsvirksomheder eksporteret som const myUtil = (…) => {…} og derefter importeres til forskellige filer. Da hvert modul er i sit eget omfang, dette leksikon Konteksten for pilefunktioner er typisk den samme som modulet, hvilket i de fleste rene værktøjer ikke engang er relevant, da de ikke behøver kontekst.
Bedste praksis med eksterne moduler og scripts
Når du serverer JavaScript på en HTML-side, er det at foretrække at indlæse det ved hjælp af eksterne filer og undgå at indlejre store indlejrede blokke. Brug også attributten type="modul" i script-tagget aktiverer moderne syntaks import Eksport og gør det klart, at filen udføres i tilstanden streng Standard.
Med hensyn til ydeevne, placering af modulscripts i hoved med udsætte tillader browseren at downloade og udføre koden uden at blokere rendering af HTML, samtidig med at en god brugeroplevelse opretholdes. I hele denne ordning er pilefunktioner simpelthen den mest naturlige måde at definere små stykker genanvendelig logik på.
Indsættelse og manipulation af DOM'en med moderne JavaScript
Selvom fokus i dette emne er på pilen og dennes funktioner, er det umuligt at ignorere JavaScripts rolle i DOM-manipulationMetoder som f.eks. getElementById, querySelector, sætAttribut eller ændringen af stil De er stadig grundlæggende, men i dag kombineres de normalt med pil-tilbagekald for at reagere på hændelser og opdatere brugerfladen.
Arbejde med begivenhedslyttere hjælp element.addEventListener('click', e => {...}) Dette er standardmåden til at forbinde logik med brugerinteraktioner. I disse handlere, denne Inde i arrow-funktionen vil det ikke være DOM-elementet, men den eksterne kontekst, så hvis du har brug for noden, skal du bruge selve event-parameteren. e.nuværendeMål o e.mål, i stedet for at stole på dette som med de gamle inline-attributter.
Synkron JavaScript, asynkron JavaScript og callbacks
Et andet vigtigt område, hvor pilfunktioner gør en forskel, er i asynkron programmeringJavaScript er single-threaded, og for at undgå at blokere grænsefladen delegerer det langsomme operationer til browser- eller Node-API'er, der derefter kalder dine callbacks, når de er færdige.
Send pilfunktioner som tilbagekald til sætTimeout, løfterHTTP-anmodninger eller diskadgang gør koden mere kompakt og opretholder kontekst uden yderligere tricks. I trinnet med indlejrede callbacks mod Promises og senere hen imod asynkron/afventPilfunktioner har været et vigtigt element i at gøre dataflowet læsbart.
Samlet set forståelse for, hvordan pilfunktioner indfanger dette leksikalskAt vide, hvornår de er din bedste allierede, og hvornår det er bedst at fortsætte med at stole på traditionelle funktioner, sammen med at vide, hvordan man kombinerer dem med moduler, array-metoder, uforanderlighed, klasser og event-loop'en, er det, der giver dig mulighed for at skrive virkelig solide JavaScript og React. I sidste ende handler det om at vælge den rigtige funktionsform til hver kontekst og udnytte dens syntaktiske fordele uden at falde i grænsetilfælde, hvor dens "denne" adfærd kan give bagslag. Del denne programmeringsvejledning og JavaScript-pilfunktionerne.