Pilfunktioner og nøgleordet this i JavaScript

Sidste ændring: 02/10/2026
Forfatter: C SourceTrail
  • Pilfunktioner giver en præcis syntaks og gengivelse this leksikalsk fra deres omgivende omfang, i stedet for at skabe deres egen binding.
  • Værdien af this I almindelige funktioner afhænger det af, hvordan de kaldes, hvilket påvirker funktioner, metoder, konstruktører, klasser og callbacks.
  • Pilfunktioner er ideelle til callbacks og array-metoder, men er et dårligt valg til objektmetoder, DOM-eventhandlere og konstruktører.
  • Forståelse af hvornår this er dynamisk versus leksikalsk er afgørende for at undgå subtile fejl og for at vælge mellem arrow og traditionelle funktioner.

Pilfunktioner og dette i JavaScript

Hvis du nogensinde har logget ind this i forskellige JavaScript-funktioner og fået vidt forskellige resultater, er du ikke alene. Mange udviklere støder på tilfælde, hvor en metode udskriver det forventede objekt, en pilefunktion udskriver window, og en indlejret pil peger pludselig "magisk" tilbage på det omgivende objekt. At forstå, hvorfor det sker, er nøglen til at skrive forudsigelig, fejlfri kode.

Pilfunktioner og this Søgeordsformen er en af ​​de vigtigste (og mest misforståede) kombinationer i moderne JavaScript. Pilfunktioner ligner blot en kortere syntaks, men under motorhjelmen ændrer de hvordan this håndteres, hvordan callbacks opfører sig, og selv hvornår du bør eller ikke bør bruge dem som metoder. Lad os gennemgå alt trin for trin, fra syntaks til udførelseskontekst, ved hjælp af almindeligt engelsk og masser af praktiske eksempler.

Pilfunktionssyntaks uden forvirring

Pilfunktioner er funktionsudtryk skrevet med => syntaks i stedet for function nøgleord. Konceptuelt kan man tænke på dem som en kompakt måde at skrive: "tag disse parametre, evaluer dette udtryk eller denne kodeblok, og returner en værdi." Nedenunder er de stadig funktioner, men de opfører sig forskelligt på flere vigtige måder.

Den mest basale pilefunktion knyttes direkte til et regulært funktionsudtryk. For eksempel dette klassiske funktionsudtryk:

const multiplyByTwo = function (value) { return value * 2; };

Kan omskrives som en pilefunktion sådan her:

const multiplyByTwo = (value) => { return value * 2; };

Pilfunktioner fremstår tydeligt, når kroppen er et enkelt udtryk. Hvis brødteksten kun er én sætning, der returnerer noget, kan du udelade både de krøllede parenteser og den eksplicitte return, hvilket muliggør en implicit returnering:

const multiplyByTwo = value => value * 2;

Når der er præcis én parameter, kan du udelade de omgivende parenteser, men kun i det specifikke tilfælde. So x => x * 2 er gyldig, men hvis du har nul eller flere parametre, skal du beholde parenteserne:

  • Nul parametre: () => 42
  • Én parameter: x => x * 2 or (x) => x * 2
  • To eller flere parametre: (x, y) => x + y

Når du har brug for mere end én sætning i brødteksten, skal du bruge krøllede parenteser og en eksplicit return. I den situation opfører pilfunktioner sig som almindelige funktioner med hensyn til returneringer: nej return, ingen værdi returneret.

const feedCat = (status) => {
if (status === 'hungry') {
return 'Feed the cat';
} else {
return 'Do not feed the cat';
}
};

Vær forsigtig, når du returnerer objektliteraler fra pilefunktioner, da objektets parenteser kan forveksles med en funktionsbrødtekst. For at undgå denne tvetydighed skal du sætte objektliteralen i parenteser, så JavaScript ved, at det er et udtryk, der skal returneres:

const toObject = value => ({ result: value });

En ting mere: pilfunktioner er altid udtryk, aldrig deklarationer. Det betyder, at de skal tildeles en variabel, egenskab eller sendes som et argument; de kan ikke stå alene som function myFunc() {}, og de hoistes ikke på samme måde som funktionsdeklarationer, så du kan ikke kalde dem, før de er defineret.

Hvad der præcist er this i JavaScript?

Nøgleordet this er en dynamisk binding, som JavaScript opretter for dig, når det udfører en funktion eller en klassemetode. Du kan tænke på det som en usynlig parameter, hvis værdi afhænger af, hvordan og hvor funktionen kaldes. Dette gør den kraftfuld og fleksibel, men også en stor kilde til forvirring.

I en ikke-strict funktion, this opløses altid til en eller anden form for objekt; i streng tilstand kan det bogstaveligt talt være enhver værdi, inklusive undefined. JavaScript bestemmer denne værdi baseret på udførelseskonteksten: almindelig funktion, metodekald, konstruktørkald, klasse, globalt omfang eller pilefunktion.

På det øverste niveau af et klassisk script (ikke et modul), this refererer til globalThis, som normalt er browserens window objekt. Så følgende sammenligning i en browser vil være sand:

console.log(this === window); // true

I funktioner, der ikke er pile, this bestemmes udelukkende af opkaldsstedet. Hvis du ringer obj.method(), så indeni method værdien af this is objHvis du tager den samme funktion og kalder den standalone som fn() i streng tilstand, this bliver undefined; i ikke-strict-tilstand "erstatter" JavaScript this med globalThis.

Det vigtigste er ikke, hvor funktionen er defineret, men hvordan den kaldes. En metode kan forblive i prototypekæden eller blive omfordelt til et andet objekt og stadig se this som det objekt, der rent faktisk bruges på kaldetidspunktet. At videregive en metode ændrer ofte dens this medmindre du eksplicit retter det.

Der er også værktøjer til at kontrollere this eksplicit: call, apply, bindog Reflect.apply. Disse lader dig "injicere" den ønskede this værdi: fn.call(obj, arg1, arg2) vil udføre fn med this indstillet til objDe samme substitutionsregler gælder i ikke-streng tilstand: hvis du består null or undefined as this, de bliver erstattet med globalThis; primitiver bliver indrammet i deres wrapper-objekter.

Tilbagekald tilføjer et ekstra lag af indirektion, fordi this styres af den, der ringer til dit tilbagekald. Array iterationsmetoder, den Promise konstruktør og lignende API'er kalder normalt callbacks med this indstillet til undefined (eller det globale objekt i sjusket tilstand). Nogle API'er, som f.eks. Array.prototype.forEach or Set.prototype.forEach, accepterer en separat thisArg parameter, du kan bruge til at indstille tilbagekaldets this.

Andre API'er kalder bevidst tilbagekald med brugerdefinerede this værdier. For eksempel reviver argument til JSON.parse og replacer forum JSON.stringify modtage this bundet til det objekt, der ejer den egenskab, der aktuelt behandles. Hændelseshandlere i DOM'en er bundet til det element, de er knyttet til, når de skrives på den "klassiske" måde.

Kerneideen: pilefunktioner opretter ikke deres egne this

Det definerende træk ved pilefunktioner er, at de aldrig opretter en ny this bindende. I stedet lukker de (eller "fanger") this fra det omgivende leksikalske miljø i det øjeblik, de oprettes. Når pilen udføres senere, genbruger den blot den registrerede værdi, uanset hvordan du kalder den.

I praksis opfører en pilefunktion sig, som om den var permanent automatisk bundet til this af dens ydre omfang. Derfor metoder som call, applyog bind kan ikke ændre sig this for en pilfunktion: thisArg argumentet ignoreres simpelthen. Du kan stadig sende almindelige parametre gennem dem, men this værdien er låst.

Overvej dette kodestykke i det globale omfang af en scriptfil:

const arrow = () => console.log(this);
arrow();

Fordi pilen er defineret i global kode, er dens this er den globale this (typisk window i et browserscript), og det ændrer sig aldrig. Opkald arrow Som en almindelig funktion vil tildeling til en egenskab eller videresendelse altid logge det samme globale objekt, når det kaldes i denne kontekst.

Den virkelig interessante adfærd opstår, når du indlejrer pilefunktioner i almindelige funktioner eller metoder. Da pilen indfanger den ydre funktions this, bliver det et effektivt værktøj til callbacks, der skal referere tilbage til deres indeholdende objekt uden den sædvanlige .bind(this) ceremoni.

const counter = {
id: 42,
start() {
setTimeout(() => {
console.log(this.id); // uses counter.id
}, 1000);
},
};

If start brugte en traditionel anonym funktion indeni setTimeout, skal du binde manuelt this eller gemme den til en variabel. Med pile arver tilbagekaldet naturligt this fra start, Hvilket er counter, Så this.id udskrifter 42 efter hensigten.

Denne leksikalske binding forklarer også det klassiske "hvorfor gør this "change"-spørgsmålet, når du bruger pile i objektliteraler. Se på disse to objekter:

const obj1 = {
speak() {
console.log(this);
}
};

const obj2 = {
speak: () => {
console.log(this);
}
};

Opkald obj1.speak() udskrifter obj1, Fordi speak er en almindelig metode og this er indstillet baseret på opkaldsstedet. Derimod obj2.speak() logger det ydre this (tit window i browsere), fordi pilen ikke bruger objektet som sin thisSelve objektliteralen opretter ikke en ny this omfang; kun funktionsdelen gør det, og pilefunktioner springer dette trin over.

Overvej nu en objektmetode, der opretter og straks kalder en indre pil:

const obj3 = {
speak() {
(() => {
console.log(this);
})();
}
};

obj3.speak();

I denne situation arver den indre pilfunktion this fra speak, Hvilket er obj3 når det kaldes som obj3.speak(). Selvom pilen er en indlejret, øjeblikkeligt aktiveret funktion, peger den stadig på obj3, ikke det globale objekt. Det er essensen af ​​leksikalsk this: den følger det omgivende oscilloskop, ikke selve pilens kaldested.

this på tværs af funktioner, objekter og konstruktører

At virkelig mestre pilfunktioner og this, det hjælper at se, hvordan this fungerer i alle større kontekster: almindelige funktioner, metoder, konstruktører, klasser og det globale omfang. Når disse regler er klare, er pilens adfærd meget lettere at ræsonnere om.

I en almindelig funktion (ikke-pil), this afhænger 100% af, hvordan funktionen kaldes. Hvis du ringer fn() i streng tilstand, this is undefined; i sjusket tilstand skaber substitution this bliver globalThis. Hvis du ringer obj.fn(), derefter this is objFlyt fn til et andet objekt eller til en variabel, og værdien af this vil bevæge sig i overensstemmelse hermed.

I en metode defineret på en objektliteral, this er det objekt, metoden tilgås på, ikke nødvendigvis det, hvor metoden oprindeligt blev defineret. If obj.__proto__ indeholder en metode, og du kalder obj.method(), så indeni method, this is obj, ikke prototypen.

Konstruktører er et andet specialtilfælde: når du kalder en funktion med new, this er bundet til den nyligt oprettede objektinstans. For eksempel, i function User(name) { this.name = name; }, ringer new User('Alex') sæt this til den nye User objekt. Hvis konstruktøren eksplicit returnerer et ikke-primitivt objekt, erstatter det returnerede objekt this som den endelige værdi af new ekspression.

Klassesyntaks bygger på disse regler med to hovedkontekster: instans og statisk. Inde i en konstruktør eller en instansmetode, this peger på den klasseinstans, du arbejder med. Indenfor statiske metoder eller statiske initialiseringsblokke, this refererer til selve klassen (eller den afledte klasse, når den kaldes via arv). Instansfelter evalueres med this bundet til den nye instans; statiske felter ser this som klassekonstruktøren.

Afledte klassekonstruktører opfører sig lidt anderledes: indtil du kalder super(), der er intet brugbart this. Start af super() initialiseres this ved at delegere til basiskonstruktøren; det er kun tilladt at returnere før det i en afledt konstruktør, hvis du eksplicit returnerer et andet objekt.

I den globale kontekst, this afhænger af, hvordan JavaScript-miljøet ombryder og udfører din kode. I et klassisk browserscript, topniveau this er det globale objekt; i et ES-modul, topniveau this er altid undefinedNode.js CommonJS-moduler er internt indpakket og udføres normalt med this indstillet til module.exportsInline event handler-attributter i HTML udføres med this indstillet til det element, de er knyttet til.

En subtil, men vigtig detalje: objektliteraler introducerer ikke i sig selv en ny this rækkevidde. Skrivning const obj = { value: this }; inde i et script vil lave obj.value lig med den ydre this, ikke objektet. Kun funktionslegemer (og klasselegemer) opretter en dedikeret this binding; pile springer bevidst dette trin over og arver.

Hvorfor pilfunktioner er gode til tilbagekald (og hvornår de ikke er det)

Fordi pilefunktioner lukker sig ind over this, de er perfekte til mange callback-scenarier, hvor du ønsker, at callback-et fortsat skal referere til det omgivende objekt eller den omgivende kontekst. Dette er især praktisk med timere, promises og array-metoder som f.eks. map, filterog reduce.

Forestil dig en metode, der skal opdatere en egenskab gentagne gange ved hjælp af setInterval. Ved at bruge en traditionel funktion, this inde i callback-systemet ville standardværdien være det globale objekt (eller være undefined i streng tilstand), så this.count ville ikke pege på din instans. Med en pilfunktion bruger tilbagekaldet naturligt this af den ydre metode.

function Counter() {
this.count = 0;

setInterval(() => {
this.count++;
}, 1000);
}

Takket være pilen, this inden for intervallet refererer tilbagekaldet til Counter eksempel, ikke window. Hvis det tilbagekald var en almindelig funktion, ville du enten have brug for .bind(this) eller en mellemliggende variabel som const self = this; at bevare referencen.

Pilfunktioner forenkler også kode ved hjælp af array-metoder, hvor man ofte er ligeglad this overhovedet. Når du sender en traditionel funktion som et callback, er den implicitte this er normalt undefined, og det glemmer du måske. Pile gør det visuelt tydeligt, at funktionen blot er en ren kortlægning af input til output.

const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);

Der er dog vigtige tilfælde, hvor pilefunktioner er det forkerte valg, især når du har brug for en dynamisk this. To klassiske antimønstre bruger pilfunktioner som objektmetoder og som DOM-hændelseshåndterere, der er afhængige af this at være elementet.

Overvej et objekt, der sporer en kats liv:

const cat = {
lives: 9,
jump: () => {
this.lives--; // bug: this is not cat
},
};

cat.jump();

Siden jump er en pil, this refererer ikke til cat men til hvad som helst this var der, hvor objektliteralen blev oprettet (ofte det globale objekt). Den tilsigtede this.lives-- enten kaster (i streng tilstand) eller muterer stille og roligt noget irrelevant. Brug af en almindelig metodesyntaks her er det korrekte træk.

DOM-hændelseslyttere er ens: standardmønsteret this.classList.toggle('on') inden for et event-tilbagekald er afhængigt af this at være det element, der udløste begivenheden. Med en pilefunktion, this peger ikke længere på elementet, så koden går i stykker.

const button = document.getElementById('press');

button.addEventListener('click', () => {
this.classList.toggle('on'); // this is not button
});

I denne situation bør handleren være en normal funktion, således at this er bundet af browseren til knapelementet. Pilfunktioner fungerer simpelthen ikke som drop-in-erstatninger, hvis din logik forventer det. this at være det dynamiske begivenhedsmål.

En anden subtil ulempe er, at pilefunktioner er syntaktisk anonyme. De har normalt ikke deres eget navn (ud over eventuelle variabler, de er tildelt), hvilket kan gøre stakspor lidt mindre beskrivende og rekursion lidt mere vanskeligt. I det meste kode i den virkelige verden er det en overkommelig afvejning, men det er værd at huske.

Særlige tilfælde: gettere, settere, bundne metoder og ulige hjørner

Gettere og settere følger den samme "call site"-regel: this er det objekt, hvor egenskaben tilgås, ikke det, hvor den oprindeligt blev defineret. Hvis en getter arves fra en prototype, og du kalder den på et afledt objekt, this Inde i getteren refererer det til det afledte objekt.

Bundne metoder oprettet med Function.prototype.bind give dig en adfærd, der ligner pilefunktioner, men på niveau med normale funktioner. Når du ringer f.bind(obj), opretter du en ny funktion, hvis this er permanent fastgjort til obj, uanset hvordan den kaldes. Dette kan være nyttigt i klasser, når du har brug for at bevare this selvom en metode er adskilt.

class Example {
constructor() {
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
console.log(this); // always the instance
}
}

Ulempen ved både bundne metoder og pilefunktioner, der bruges som instansfelter, er, at hver instans får sin egen kopi af funktionen, hvilket kan øge hukommelsesforbruget. Denne afvejning er normalt acceptabel, når du kun binder et lille antal ofte frakoblede metoder, men det er noget, man skal være opmærksom på i ydeevnekritisk kode.

Der er også nogle ældre hjørnesager, hvor this opfører sig anderledes, f.eks. inden for en udfaset with udmelding. Inde i en with (obj) { ... } blok, kalder en funktion, der er en egenskab af obj opfører sig effektivt, som om du havde skrevet obj.method(), Så this er bundet til objModerne kode bør undgå with, men forståelsen af ​​denne undtagelse tydeliggør, at this afhænger stadig fundamentalt af, hvordan et funktionskald er dannet.

Inline-hændelseshandlere i HTML har også en særlig regel: den omgivende inline-handlerkode ser this som elementet, men indre funktioner defineret i den pågældende handler falder tilbage til det almindelige this regler. Så en indre traditionel funktion, der ikke er bundet til noget, vil normalt se this as globalThis (eller undefined i streng tilstand), ikke elementet.

Husk endelig, at pilefunktioner ikke har en prototype ejendom og kan ikke bruges som konstruktører med new. Forsøg på new MyArrow() vil kaste en TypeError. Hvis du har brug for en funktion, der kan fungere som en konstruktør, skal du bruge en almindelig funktion eller en klasse.

Når man husker på disse detaljer, bliver det meget nemmere at vælge mellem pilefunktioner og traditionelle funktioner. Brug pile, hvor du vil have leksikalsk this og præcis syntaks, og vend tilbage til almindelige funktioner, når du har brug for den dynamiske, kalds-site-drevne this adfærd eller konstruktørsemantik.

Når du først har internaliseret, hvordan this er bundet i hver situation, bliver pilefunktioner en stærk allieret i stedet for en overraskende kilde til fejl. De strømliner almindelige mønstre som callbacks og simple transformationer, mens almindelige funktioner fortsat håndterer roller, der afhænger af deres egne funktioner. this binding, såsom metoder, konstruktører og dynamiske hændelseshåndterere.

Relaterede indlæg: