Funkcije strelica i `this` u JavaScriptu: cjeloviti vodič

  • Vrijednost this U JavaScriptu, to ovisi o tome kako se funkcija poziva, a varira između globalnog konteksta, metoda objekta i korištenja strogog načina rada.
  • Funkcije strelica ne stvaraju vlastite thisUmjesto toga, oni leksički nasljeđuju terminologiju domene u kojoj su definirani, što izbjegava mnoge probleme u povratnim pozivima.
  • Preporučuje se korištenje funkcija strelica u povratnim pozivima i metodama nizova, ali ih izbjegavajte kao metode objekata, konstruktore ili DOM rukovatelje događajima koji zahtijevaju this dinamičan.

Ilustracija strelice i ove funkcije u JavaScriptu

Ako već neko vrijeme programirate u JavaScriptu, sigurno ćete prepoznati ključnu riječ Ovo ti je zadalo više od jedne glavoboljeA otkad su se u ES6 pojavile funkcije strelica, stvari su postale još kompliciranije... ili jednostavnije, ovisno o tome kako gledate na to.

U ovom članku ćemo detaljnije pogledati kako to funkcionira To se odnosi na tradicionalne funkcije i funkcije strelica.Zašto se ponekad čini da pokazuje na određeni objekt, a ponekad na globalni objekt, i u kojim situacijama ima smisla koristiti funkcije strelica, a u kojim ih je bolje izbjegavati?

Što točno this u JavaScriptu

Pridržana riječ this To je referenca na kontekst izvršenja funkcije koja se trenutno izvršava. Za razliku od drugih jezika, u JavaScriptu odluka se ne temelji na tome gdje je funkcija definirana, već na izvršavanju funkcije. kako prizvati.

To znači da se ista funkcija može pozvati na različite načine, i na svaki od njih, this može pokazati na drugi objektTo nije nešto što možete promijeniti izravnim zadatkom (ne možete to učiniti this = algo), ali na to možete utjecati specifičnim mehanizmima kao što su call, apply y bind.

Nadalje, njihovo ponašanje varira između strogi način rada i nestrogi način radaU nestrogom načinu rada, ako pozovete funkciju "golu" (bez objekta ispred nje), this Obično je to globalni objekt (u pregledniku, window), dok u strogom načinu rada može biti undefinedOva razlika je važna pri usporedbi primjera koda iz različitih izvora.

To u globalnom kontekstu i u normalnim funkcijama

U preglednicima, kada niste unutar modula ili funkcije, globalni kontekst je objekt windowi tamo this pokažite na taj objektTo jest, ako u konzolu upišete sljedeće:

console.log(this === window); // true en un entorno de navegador no estricto

Unutar funkcije deklarirane na "klasičan" način (normalna funkcija), vrijednost this Ovisi kako se ta funkcija zove.Ako ga pozovete bez prethodnog objekta, u nestrogom načinu rada this Obično je to globalni, i strogo govoreći, bit će undefinedZato je ponekad, prilikom premještanja koda s jedne web-lokacije na drugu, Ovo više nije ono što ste očekivali..

Ovo u metodama objekta definiranim s normalnim funkcijama

Kada definirate metodu na objektu koristeći tradicionalnu sintaksu, this unutar metode, referenca na sam objekt iz kojeg je ta metoda pozvana.

Na primjer, ako imate nešto poput:

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

Poziv obj.speak() pravi this unutar speak budi taj/ta/to objOvo je ponašanje koje ljudi obično intuitivno očekuju: metoda govori „u ime“ objekta.

Ako koristite klasičnu funkciju umjesto skraćene sintakse, učinak je isti, jer Ključ leži u načinu pozivanja metodeNije važno jeste li koristili kraticu metode ili ključnu riječ function unutar objekta.

Ovo u metodama definiranim pomoću funkcija strelica

Stvari se mijenjaju kada definirate metodu pomoću funkcije strelice. Nešto poput:

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

U ovom slučaju, prilikom izvršavanja obj2.speak() vidjet ćeš to this Više nije obj2ali vanjski leksički kontekst tom objektu, koji je u klasičnom skriptu preglednika obično globalni objekt window.

Ovo je zbunjujuće prvi put kada to vidite, jer očekujete da metoda objekta pokazuje na sam objekt. Međutim, Funkcije strelica ne stvaraju vlastite thisOni nasljeđuju vrijednost this opsega u kojem su definirani. Ako je taj opseg globalan, nasljeđuju globalni opseg; ako je neki drugi, naslijedit će taj drugi opseg.

Stoga se u modernoj dokumentaciji često ponavlja preporuka: Ne koristite funkcije strelica kao metode objekta kada ti treba this ciljati na taj objekt.

Leksički opseg this funkcije strelica

Ključna razlika između normalnih funkcija i funkcija strelica je u tome što ove potonje imaju leksičku vezu za thisJednostavno rečeno: oni ne odlučuju o svom this ne kada se međusobno zovu, već kada stvoriti.

Zamislite ovaj primjer:

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

Ovdje bi se moglo činiti da, kao i unutar speak izvršavamo funkciju strelice, Ovo bi se trebalo "resetirati" na globalnoAli događa se upravo suprotno: funkcija strelice hvata this funkcije koja ga okružuješto je u ovom slučaju metoda speak priziva se kao obj3.speak()Stoga, vrijednost this Onaj prikazan na konzoli je onaj iz obj3.

To jest, funkcije strelica nemaju vlastite thisveć radije ponovno koriste ono iz svoje neposredne okolineOvo je nevjerojatno korisno u ugniježđenim povratnim pozivima, timerima, obećanjima i bilo gdje drugdje gdje ste se, s klasičnim funkcijama, morali boriti s .bind ili s trikovima poput const that = this;.

Praktični primjeri gubitka i očuvanja this

Jedan od klasičnih problema u JavaScriptu je taj što se prilikom definiranja funkcije unutar metode, gubiš referencu na this koji je pokazivao na objekt i završite s globalnim ili s undefined.

Uzmimo tipičan slučaj korištenja setTimeout unutar metode objekta s tradicionalnom funkcijom:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    setTimeout(function() {
      console.log(this.nombre);
    }, 3000);
  }
};
persona.decirNombre(); // Muestra undefined

ovdje ovo unutar funkcije proslijeđene setTimeout To više nije objekt personaTa se funkcija povratnog poziva izvršava u globalnom kontekstu (u pregledniku, window), dakle this.nombre Pokušava pročitati svojstvo u globalnoj promjenji, koje ne postoji, i na kraju dobije undefined.

Prije nego što su postojale funkcije strelica, uobičajeno rješenje bilo je pohranjivanje vrijednosti this u pomoćnoj varijabli da biste ga "povukli" u funkciju:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    let that = this; // aquí this es persona
    setTimeout(function() {
      console.log(that.nombre);
    }, 3000);
  }
};

Zahvaljujući toj varijabli, održava se ispravna referenca na objekt. Ali to je pomalo ružan i repetitivan trik. S funkcijama strelica, ovaj problem je uvelike pojednostavljen:

const persona = {
  nombre: 'Agustin',
  decirNombre: function() {
    setTimeout(() => {
      console.log(this.nombre);
    }, 3000);
  }
};

Ovdje funkcija strelice ne stvara vlastitu thistako nasljeđuje this metode decirNombrekoji je objekt personaRezultat: „Agustin“ se ispravno prikazuje bez potrebe za međuvarijablom ili .bind.

poziv, primjena i povezivanje: upravljanje vrijednošću this

Uz "prirodni" način postavljanja konteksta pozivom metode, JavaScript nam daje alate za prisiliti vrijednost this u normalnim funkcijama: call, apply y bind.

Metode call() y apply() Oni odmah pozivaju funkciju, omogućujući vam da proslijedite objekt koji želite koristiti kao this. Razlika je u tome call prima argumente jedan po jedan, dok apply Primaju se u nizu. bind(), umjesto toga, vraća novu funkciju s this „priloženo“ vrijednosti koju ste navelipa je možeš kasnije nazvati kad ti odgovara.

Međutim, s funkcijama strelica ove metode nisu korisne za promjenu this jer je njegova vrijednost leksički povezana. Možeš koristiti call, apply o bind prosljeđivati ​​argumente, ali ne i mijenjati kontekst funkcija strelica, što je vrlo važna razlika u odnosu na regularne funkcije.

Osnovna sintaksa funkcija strelica

Osim ponašanja thisFunkcije strelica pružaju kompaktnija i izražajnija sintaksa za mnoge situacije. Opći oblik je:

(arg1, arg2, ..., argN) => expresion

Ovaj oblik automatski vraća rezultat izraza desno od strelice, dakle Nema potrebe pisati riječ return kada imate samo jedan jednostavan izraz.

Neke uobičajene sintaktičke točke:

  • Bez parametara:
    () => 42 ili čak _ => 42 ako vam nije važno ime argumenta.
  • S jednim parametrom:
    Zagrade nisu obavezne; možete napisati x => x * 2 o (x) => x * 2.
  • S više parametara:
    Zagrade su obavezne: (x, y) => x + y.

Kada vam je potrebno više izjava, možete koristiti tijelo bloka s ključevima:

const sumar = (x, y) => {
  const resultado = x + y;
  return resultado;
};

U ovom slučaju, budući da postoje ključevi, Više nema implicitnog povrata; ako ne stavite returnfunkcija će vratiti undefinedTo se odnosi i na funkcije strelica i na tradicionalne funkcije.

Vratite literalne objekte pomoću funkcija strelica

Postoji mali, ali vrlo čest sintaktički detalj: kada funkcija strelice vrati doslovni objekt izravnoMorate ga staviti u zagrade kako ga interpreter ne bi zamijenio za blok.

Na primjer:

x => ({ y: x })

Bez tih zagrada, JavaScript bi vitičaste zagrade interpretirao kao početak tijela funkcije, a ne kao objekt. To je jednostavan trik, ali uzrokuje mnogo glupih pogrešaka ako ga zaboravite.

Funkcije strelica: anonimne i bez prototipa

Funkcije strelica su sintaktički anonimnoNemaju vlastita imena, što može donekle zakomplicirati stvari. poruke o pogreškama i otklanjanju pogrešakajer u tragu ne vidite izravno identifikator funkcije, osim ako ga niste dodijelili konstanti s prepoznatljivim imenom.

Osim toga, funkcije strelica Ne posjeduju imovinu prototype i ne mogu se koristiti kao građevinske tvrtkeAko ih pokušate prizvati s newDobit ćete grešku. Za stvaranje objekata pomoću konstruktora ili klasa i dalje morate koristiti uobičajene funkcije ili sintaksu. class.

Druga posljedica je da Nisu prikladni za uzorke koji zahtijevaju unutarnje samoreferenciranje., kao što su neki oblici rekurzije ili rukovatelji događajima koji se moraju odjaviti pomoću this ili vlastito ime funkcije.

Gdje funkcije strelica sjaju

Velika snaga funkcija strelica je upravo u njihovoj leksičko povezivanje thisIdealni su u situacijama kada želite da povratni poziv koji prosljeđujete drugoj funkciji održava this okolnog područja.

Na primjer, u objektu s metodom koja pokreće timer i treba mu stalno pristupati svojstva samog objekta pomoću this:

const contador = {
  id: 42,
  iniciar() {
    setTimeout(() => {
      console.log(this.id); // this es contador
    }, 1000);
  }
};

U ES5 je bilo uobičajeno staviti .bind(this) na povratni poziv ili spremi this u drugoj varijabli. S funkcijama strelica, Kod postaje čišći i bliži stvarnoj namjeri..

Također su vrlo praktični s metodama nizova kao što su map, filter, reduce i društvo, jer smanjiti sintaktičku buku kada je logika funkcije kratka:

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

Kada se koriste štedljivo, ovi kompaktni oblici olakšavaju praćenje toka podataka na prvi pogled.

Kada izbjegavati funkcije strelica

Iako su streličaste funkcije vrlo korisne, one nisu zamjena za regularne funkcije. Postoji nekoliko jasnih slučajeva gdje Najbolje ih je ne koristiti.:

  • Metode objekta koje ovise o this:
    Ako definirate metodu kao saltos: () => { this.vidas--; } unutar objekta gato, this Neće pokazivati ​​na mačku, već na vanjsko okruženje, a svojstvo se neće ažurirati kako očekujete.
  • Povratni pozivi DOM događaja kojima je potreban this dinamičan:
    U rukovatelju poput boton.addEventListener('click', () => { this.classList.toggle('on'); });, this Neće biti pritisnuti gumb, već viši kontekst, što će vam vjerojatno dati grešku u tipu.
  • Graditelji ili funkcije koje trebaju prototype:
    Budući da se ne može koristiti s newFunkcije strelica nisu prikladne za stvaranje instanci ili za uzorke temeljene na prototipovima.

U svim tim slučajevima, a Normalna funkcija ostaje odgovarajući alat jer to omogućuje this Dinamički je povezan s načinom na koji pozivate funkciju.

Ako se naviknete svjesno birati između normalne funkcije i funkcije strelice, ovisno o tome što vam je potrebno this i iz konteksta, Vaš će kod biti predvidljiviji i čitljiviji. za svakoga tko ga nakon toga zadrži.

U konačnici, razumijevanje kako vrijednost this u JavaScriptu i kako funkcije strelica nasljeđuju tu vrijednost Ključ za sprječavanje neočekivanih rezultata, iskorištavanje sintaktičkog šećera ES6-a i pisanje metoda, povratnih poziva i rukovatelja događajima koji rade upravo ono što ste imali na umu jest razumijevanje leksičkog opsega u kojem su stvoreni.