Ako već neko vrijeme programirate u JavaScriptu, sigurno ćete prepoznati ključnu riječ Ovo ti je zadalo više od jedne glavoboljeA otkako su se funkcije strelica pojavile u ES6, stvari su postale još komplikovanije... ili jednostavnije, ovisno o tome kako gledate na to.
U ovom članku ćemo detaljnije pogledati kako to funkcioniše Ovo se odnosi na tradicionalne funkcije i funkcije sa strelicama.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?
Sta tacno this u JavaScriptu
Rezervisana reč this To je referenca na kontekst izvršenja funkcije koja se trenutno izvršava. Za razliku od drugih jezika, u JavaScriptu odluka se ne zasniva na tome gdje je funkcija definirana, već na njenom izvršavanju. 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 ukazivati na drugi objekatTo nije nešto što možete promijeniti direktnim 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 nestriktnom režimu, ako pozovete funkciju "golu" (bez objekta ispred nje), this Obično je to globalni objekat (u pretraživaču, window), dok u strogom režimu može biti undefinedOva razlika je važna prilikom poređenja primjera koda iz različitih izvora.
Ovo u globalnom kontekstu i u normalnim funkcijama
U preglednicima, kada niste unutar nijednog modula ili funkcije, globalni kontekst je objekt window, a tu this pokažite na taj objektTo jest, ako u konzolu ukucate 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 Zavisi od toga kako se ta funkcija zove.Ako ga pozovete bez prethodnog objekta, u nestriktnom režimu this Obično je to globalni, i strogo govoreći, bit će undefinedZato je ponekad, prilikom premještanja koda s jedne stranice na drugu, Ovo više nije ono što ste očekivali..
Ovo je u metodama objekta definiranim s normalnim funkcijama
Kada definirate metodu na objektu koristeći tradicionalnu sintaksu, this unutar metode, referenca na sam objekat 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 unutra 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, efekat je isti, jer Ključ leži u načinu na koji se metoda pozivaNije važno jeste li koristili skraćenicu 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() videćete 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 objekat. Međutim, Funkcije strelica ne kreiraju vlastite thisOni nasljeđuju vrijednost this opsega u kojem su definirani. Ako je taj opseg globalan, oni nasljeđuju globalni opseg; ako je neki drugi, naslijedit će taj drugi opseg.
Stoga se često ponavljana preporuka u modernoj dokumentaciji glasi: 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 streličkih funkcija je u tome što ove druge 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 trebalo "resetirati" na globalnoAli dešava 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.
Mislim Funkcije strelica nemaju svoje vlastite thisveć radije ponovo koriste ono iz svoje neposredne okolineOvo je nevjerovatno korisno u ugniježđenim povratnim pozivima, tajmerima, obećanjima i bilo gdje drugdje gdje ste se, s klasičnim funkcijama, morali boriti sa .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, prilikom definiranja funkcije unutar metode, gubiš referencu na this koji je ukazivao na objekat 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 funkcija povratnog poziva izvršava se u globalnom kontekstu (u pregledniku, window), dakle this.nombre Pokušava pročitati svojstvo u globalnoj promjenjivoj promjenjivoj, koje ne postoji, i na kraju dobije undefined.
Prije nego što su postojale funkcije strelica, uobičajeno rješenje je bilo pohranjivanje vrijednosti this u pomoćnoj varijabli da ga "prevučete" 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 objekat. Ali to je pomalo ružan i repetitivan trik. Sa strelicama, ovaj problem je znatno pojednostavljen:
const persona = {
nombre: 'Agustin',
decirNombre: function() {
setTimeout(() => {
console.log(this.nombre);
}, 3000);
}
};
Ovdje funkcija strelice ne kreira vlastitu this, tako nasljeđuje this metode decirNombrekoji je objekat personaRezultat: „Agustin“ se prikazuje ispravno bez potrebe za međuvarijablom ili .bind.
poziv, primjena i povezivanje: kontrola vrijednosti this
Pored "prirodnog" načina 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ćavajući vam da proslijedite objekat 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 sa this „priloženo“ vrijednosti koju ste navelitako da je možeš kasnije pozvati kada ti odgovara.
Međutim, kod funkcija sa strelicama 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, tako da Nema potrebe pisati riječ return kada imate samo jedan jednostavan izraz.
Neke uobičajene sintaktičke tačke:
- Bez parametara:
() => 42ili čak_ => 42ako te nije briga za naziv argumenta. - Sa jednim parametrom:
Zagrade su opcionalne; možete napisatix => x * 2o(x) => x * 2. - Sa više parametara:
Zagrade su obavezne:(x, y) => x + y.
Kada vam je potrebno više izjava, možete koristiti tijelo bloka sa 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 povratkaako ne stavite returnfunkcija će vratiti undefinedOvo 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 objekat direktnoMorate ga staviti u zagrade kako ga interpreter ne bi pomiješao s blokom.
Na primjer:
x => ({ y: x })
Bez tih zagrada, JavaScript bi interpretirao vitičaste zagrade kao početak tijela funkcije, a ne kao objekt. To je jednostavan trik, ali uzrokuje mnogo glupih greš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 greškama i debug porukamajer u tragu ne vidite direktno 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 kompanijeAko pokušate da ih prizovete sa newDobit ćete grešku. Da biste kreirali objekte pomoću konstruktora ili klasa, i dalje morate koristiti uobičajene funkcije ili sintaksu. class.
Druga posljedica je da Nisu pogodni za obrasce koji zahtijevaju interno 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 blistaju
Velika snaga funkcija strelica je upravo u njihovom 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 tajmer i treba stalno pristupati svojstva samog objekta koristeći 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 sačuvajte this u drugoj varijabli. Sa funkcijama strelica, Kod postaje čistiji i bliži stvarnoj namjeri..
Također su vrlo praktični s metodama nizova kao što su map, filter, reduce i kompanija, jer smanjite 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 treba izbjegavati funkcije strelica
Iako su streličke funkcije veoma korisne, one nisu zamjena za regularne funkcije. Postoji nekoliko jasnih slučajeva gdje Najbolje ih je ne koristiti.:
- Metode objekta koje zavise od
this:
Ako definirate metodu kaosaltos: () => { this.vidas--; }unutar objektagato,thisNeće pokazivati na mačku, već na vanjsko okruženje, a svojstvo se neće ažurirati kako očekujete. - DOM povratne funkcije događaja kojima je potreban
thisdinamičan:
U rukovatelju kao što jeboton.addEventListener('click', () => { this.classList.toggle('on'); });,thisNeće biti pritisnuto dugme, već viši kontekst, što će vam vjerovatno dati grešku u kucanju. - Graditelji ili funkcije koje trebaju
prototype:
Budući da se ne može koristiti sanewFunkcije strelica nisu pogodne za kreiranje instanci ili za obrasce zasnovane na prototipovima.
U svim ovim slučajevima, a Normalna funkcija ostaje odgovarajući alat jer to omogućava 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š kod će biti predvidljiviji i čitljiviji. za svakoga ko ga nakon toga sačuva.
Konačno, 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 i pisanje metoda, povratnih poziva i rukovatelja događajima koji rade upravo ono što ste imali na umu, je razumijevanje leksičkog opsega u kojem su kreirani.