- Bultu funkcijas nodrošina kodolīgu sintaksi un uztveršanu
thisleksiski no apkārtējās darbības jomas, nevis veidojot savu saistījumu. - Vērtība
thisregulārajās funkcijās ir atkarīgs no tā, kā tās tiek izsauktas, ietekmējot funkcijas, metodes, konstruktorus, klases un atzvanīšanas funkcijas. - Bultu funkcijas ir ideāli piemērotas atzvanīšanas darbībām un masīvu metodēm, bet tās ir slikta izvēle objektu metodēm, DOM notikumu apstrādātājiem un konstruktoriem.
- Izpratne par to, kad
thisIr svarīgi, lai izvairītos no smalkām kļūdām un izvēlētos starp bultiņas un tradicionālajām funkcijām, vai tā ir dinamiska vai leksiska.

Ja esat kādreiz pieteicies this dažādās JavaScript funkcijās un ieguvuši ļoti atšķirīgus rezultātus, jūs neesat viens. Daudzi izstrādātāji saskaras ar gadījumiem, kad metode izdrukā paredzēto objektu, bet bultiņas funkcija izdrukā window, un ligzdota bultiņa pēkšņi “maģiski” norāda atpakaļ uz apkārtējo objektu. Izpratne par to, kāpēc tas notiek, ir atslēga uz paredzama, kļūdu nesaturoša koda rakstīšanu.
Bultiņu funkcijas un this atslēgvārdi veido vienu no svarīgākajām (un pārprastākajām) kombinācijām mūsdienu JavaScript valodā. Bultu funkcijas izskatās pēc īsākas sintakses, taču būtībā tās maina veidu, kā this tiek apstrādāta, kā darbojas atzvanīšanas funkcijas un pat to, kad tās ir vai nav jāizmanto kā metodes. Apskatīsim visu soli pa solim, sākot no sintakses līdz izpildes kontekstam, izmantojot vienkāršu angļu valodu un daudz praktisku piemēru.
Bultas funkcijas sintakse bez neskaidrībām
Bultu funkcijas ir funkciju izteiksmes, kas rakstītas ar => sintakse, nevis function atslēgvārds. Konceptuāli tās var uztvert kā kompaktu rakstīšanas veidu: “ņem šos parametrus, novērtē šo izteiksmi vai koda bloku un atgriež vērtību.” Zemāk tās joprojām ir funkcijas, taču tās vairākos svarīgos veidos uzvedas atšķirīgi.
Visvienkāršākā bultiņas funkcija ir tieši saistīta ar regulārās funkcijas izteiksmi. Piemēram, šī klasiskā funkcijas izteiksme:
const multiplyByTwo = function (value) { return value * 2; };
Var pārrakstīt kā bultiņas funkciju šādi:
const multiplyByTwo = (value) => { return value * 2; };
Bultas funkcijas izpaužas tad, kad ķermenis ir viena izteiksme. Ja pamatteksts ir tikai viens paziņojums, kas atgriež kaut ko, varat atmest gan cirtainās iekavas, gan tiešo return, nodrošinot netiešu atgriešanos:
const multiplyByTwo = value => value * 2;
Ja ir tikai viens parametrs, apkārtējās iekavas var izlaist, bet tikai šajā konkrētajā gadījumā. So x => x * 2 ir derīgs, bet, ja jums ir nulle vai vairāki parametri, jums ir jāsaglabā iekavas:
- Nulles parametri:
() => 42 - Viens parametrs:
x => x * 2or(x) => x * 2 - Divi vai vairāki parametri:
(x, y) => x + y
Ja pamattekstā nepieciešams vairāk nekā viens apgalvojums, jāizmanto cirtaini iekavas un skaidrs return. Šādā situācijā bultiņu funkcijas attiecībā uz atgriešanas vērtībām uzvedas kā parastas funkcijas: nē return, netika atgriezta nekāda vērtība.
const feedCat = (status) => {
if (status === 'hungry') {
return 'Feed the cat';
} else {
return 'Do not feed the cat';
}
};
Esiet uzmanīgi, atgriežot objektu literāļus no bultiņu funkcijām, jo objekta figūriekavas var sajaukt ar funkcijas pamattekstu. Lai izvairītos no šīs neskaidrības, ievietojiet objekta literāli iekavās, lai JavaScript zinātu, ka tā ir atgriežamā izteiksme:
const toObject = value => ({ result: value });
Vēl viena lieta: bultiņu funkcijas vienmēr ir izteiksmes, nekad deklarācijas. Tas nozīmē, ka tie jāpiešķir mainīgajam, īpašībai vai jānodod kā arguments; tie nevar pastāvēt atsevišķi, piemēram, function myFunc() {}, un tie netiek izsaukti tāpat kā funkciju deklarācijas, tāpēc tos nevar izsaukt, pirms tie ir definēti.
Kas īsti ir this JavaScript valodā?
Atslēgvārds this ir dinamiska saistīšana, ko JavaScript izveido, izpildot funkciju vai klases metodi. To var uzskatīt par neredzamu parametru, kura vērtība ir atkarīga no tā, kā un kur funkcija tiek izsaukta. Tas padara to jaudīgu un elastīgu, taču arī rada lielu apjukumu.
Nestingrā funkcijā this vienmēr atrisinās uz kāda veida objektu; stingrā režīmā tā var būt burtiski jebkura vērtība, ieskaitot undefined. JavaScript nosaka šo vērtību, pamatojoties uz izpildes kontekstu: regulāra funkcija, metodes izsaukums, konstruktora izsaukums, klase, globāls tvērums vai bultiņas funkcija.
Klasiskā skripta (nevis moduļa) augšējā līmenī this attiecas uz globalThis, kas parasti ir pārlūkprogrammas window objekts Tātad šāds salīdzinājums pārlūkprogrammā būs patiess:
console.log(this === window); // true
Funkcijās, kas nav bultiņas, this pilnībā nosaka zvana vietne. Ja jūs zvanāt obj.method(), tad iekšā method vērtība this is objJa ņemat to pašu funkciju un saucat to par atsevišķu kā fn() stingrā režīmā, this kļūst undefined; nestingrā režīmā JavaScript “aizstāj” this ar globalThis.
Svarīgi ir tas, ka svarīgi nav tas, kur funkcija ir definēta, bet gan tas, kā tā tiek izsaukta. Metode var atrasties prototipa ķēdē vai tikt piešķirta citam objektam, tomēr to joprojām var redzēt. this kā jebkurš objekts, kas faktiski tiek izmantots izsaukuma laikā. Metodes nodošana tālāk bieži vien maina tās this ja vien jūs to nepārprotami neizlabojat.
Ir arī rīki, lai kontrolētu this nepārprotami: call, apply, bind, un Reflect.apply. Tie ļauj jums "injicēt" vēlamo this vērtība: fn.call(obj, arg1, arg2) izpildīs fn ar this iestatīts uz objNestingrā režīmā tiek piemēroti tie paši aizstāšanas noteikumi: ja jūs izturat null or undefined as this, tie tiek aizstāti ar globalThis; primitīvi tiek ievietoti to apvalka objektos.
Atzvanīšanas pievieno vēl vienu netiešības slāni, jo this kontrolē tas, kurš izsauc jūsu atzvanu. Masīvu iterācijas metodes Promise konstruktors un līdzīgas API parasti izsauc atzvanīšanas signālus ar this iestatīts uz undefined (vai globālo objektu nevērīgā režīmā). Dažas API, piemēram, Array.prototype.forEach or Set.prototype.forEach, pieņemt atsevišķu thisArg parametrs, ko var izmantot, lai iestatītu atzvanīšanas funkciju this.
Citi API apzināti izsauc atzvanīšanas funkcijas ar pielāgotu this vērtības. Piemēram, reviver arguments pret JSON.parse un replacer forums JSON.stringify saņemt this saistīts ar objektu, kuram pieder pašlaik apstrādājamais īpašums. DOM notikumu apstrādātāji ir saistīti ar elementu, kuram tie ir pievienoti, ja tie ir rakstīti “klasiskajā” veidā.
Galvenā ideja: bultiņu funkcijas nerada savas this
Bultu funkciju raksturīgā iezīme ir tā, ka tās nekad nerada jaunu this saistošs. Tā vietā viņi aizveras (jeb "notver") this no apkārtējās leksiskās vides to izveides brīdī. Kad bultiņa vēlāk tiek izpildīta, tā vienkārši atkārtoti izmanto šo uztverto vērtību neatkarīgi no tā, kā jūs to nosaucat.
Praksē bultiņas funkcija darbojas tā, it kā tā būtu pastāvīgi automātiski saistīta ar this no tā ārējās darbības jomas. Tāpēc tādas metodes kā call, apply, un bind nevar mainīties this bultiņas funkcijai: thisArg arguments tiek vienkārši ignorēts. Jūs joprojām varat caur tiem nodot regulārus parametrus, bet this vērtība ir bloķēta.
Apsveriet šo fragmentu skripta faila globālajā darbības jomā:
const arrow = () => console.log(this);
arrow();
Tā kā bultiņa ir definēta globālajā kodā, tās this ir globāls this (parasti window pārlūkprogrammas skriptā), un tas nekad nemainās. Calling arrow Kā vienkāršai funkcijai, piešķirot tai īpašību vai nododot to tālāk, šajā kontekstā izsaucot, vienmēr tiks reģistrēts viens un tas pats globālais objekts.
Patiešām interesanta uzvedība parādās, ja bultiņu funkcijas tiek ligzdotas parastajās funkcijās vai metodēs. Tā kā bultiņa attēlo ārējās funkcijas this, tas kļūst par spēcīgu rīku atzvanīšanas darbībām, kurām ir jāatsaucas uz to saturošo objektu bez ierastās .bind(this) ceremonija.
const counter = {
id: 42,
start() {
setTimeout(() => {
console.log(this.id); // uses counter.id
}, 1000);
},
};
If start iekšpusē izmantoja tradicionālu anonīmu funkciju setTimeout, jums būtu jāpiesaista manuāli this vai saglabājiet to mainīgajā. Ar bultiņām atzvanīšanas funkcija dabiski pārmanto this no start, Kas ir counter, Tik this.id izdrukas 42 kā paredzēts.
Šī leksiskā saistība izskaidro arī klasisko jautājumu “kāpēc” this jautājums "mainīt", lietojot bultiņas objektu literāļos. Apskatiet šos divus objektus:
const obj1 = {
speak() {
console.log(this);
}
};
const obj2 = {
speak: () => {
console.log(this);
}
};
Calling obj1.speak() izdrukas obj1, Jo speak ir regulāra metode, un this tiek iestatīts, pamatojoties uz izsaukuma vietni. Turpretī, obj2.speak() reģistrē ārējo this (bieži window pārlūkprogrammās), jo bultiņa neizmanto objektu kā savu thisObjekta literālis pats par sevi nerada jaunu this darbības joma; to dara tikai funkcijas pamatteksts, un bultiņu funkcijas šo soli izlaiž.
Tagad apsveriet objekta metodi, kas izveido un nekavējoties izsauc iekšējo bultiņu:
const obj3 = {
speak() {
(() => {
console.log(this);
})();
}
};
obj3.speak();
Šajā situācijā iekšējās bultiņas funkcija manto this no speak, Kas ir obj3 kad to sauc par obj3.speak(). Lai gan bultiņa ir ligzdota, nekavējoties izsaukta funkcija, tā joprojām norāda uz obj3, nevis globālais objekts. Tā ir leksiskās valodas būtība. this: tas seko apkārtējam tvērumam, nevis pašas bultiņas izsaukšanas vietai.
this visās funkcijās, objektos un konstruktoros
Lai patiešām apgūtu bultiņu funkcijas un this, ir noderīgi redzēt, kā this darbojas visos galvenajos kontekstos: regulārās funkcijās, metodēs, konstruktoros, klasēs un globālajā tvērumā. Kad šie noteikumi ir skaidri, bultiņas uzvedību ir daudz vieglāk izskaidrot.
Vienkāršā funkcijā (kas nav bultiņa) this 100% atkarīgs no tā, kā funkcija tiek izsaukta. Ja jūs zvanāt fn() stingrā režīmā, this is undefined; paviršā režīmā aizstāšana rada this kļūt globalThisJa jūs zvanāt obj.fn(), Tad this is objPārvietot fn uz citu objektu vai mainīgo un vērtību this attiecīgi pārvietosies.
Metodē, kas definēta objekta literālī, this ir objekts, kuram piekļūst metodei, ne vienmēr tas, kurā metode sākotnēji tika definēta. If obj.__proto__ satur metodi un jūs izsaucat obj.method(), tad iekšā method, this is obj, nevis prototips.
Konstruktori ir vēl viens īpašs gadījums: kad izsaucat funkciju ar new, this ir saistīts ar tikko izveidoto objekta instanci. Piemēram, in function User(name) { this.name = name; }, zvana new User('Alex') komplekti this uz jauno User objekts. Ja konstruktors nepārprotami atgriež neprimitīvu objektu, šis atgrieztais objekts aizstāj this kā galīgo vērtību new izteiksme.
Klases sintakse balstās uz šiem noteikumiem ar diviem galvenajiem kontekstiem: instances un statisko. Konstruktora vai instances metodes iekšpusē this norāda uz klases instanci, ar kuru strādājat. Statiskās metodēs vai statiskās inicializācijas blokos this attiecas uz pašu klasi (vai atvasināto klasi, ja tā tiek izsaukta mantojuma ceļā). Instances lauki tiek novērtēti ar this saistīts ar jauno instanci; statiskie lauki skat. this kā klases konstruktors.
Atvasināto klašu konstruktori uzvedas nedaudz savādāk: līdz brīdim, kad izsaucat super(), nav nekā lietojama this. Aicina super() inicializē this deleģējot bāzes konstruktoram; atgriešana pirms šīs darbības veikšanas atvasinātajā konstruktorā ir atļauta tikai tad, ja jūs skaidri atgriežat citu objektu.
Globālā kontekstā this atkarīgs no tā, kā JavaScript vide ietver un izpilda jūsu kodu. Klasiskā pārlūkprogrammas skriptā augstākā līmeņa this ir globāls objekts; ES modulī augstākā līmeņa this vienmēr undefinedNode.js CommonJS moduļi ir iekšēji ietīti un parasti tiek izpildīti ar this iestatīts uz module.exportsHTML iekļautie notikumu apstrādātāja atribūti tiek izpildīti ar this iestatīts uz elementu, pie kura tie ir pievienoti.
Viena smalka, bet svarīga detaļa: objektu literāļi paši neievieš jaunu this darbības joma. Rakstiski const obj = { value: this }; skripta iekšpusē tiks izveidots obj.value vienāds ar ārējo this, nevis objektu. Tikai funkciju ķermeņi (un klases ķermeņi) izveido īpašu this iesiešana; bultiņas apzināti izlaiž šo soli un manto.
Kāpēc bultiņu funkcijas ir lieliskas atzvanīšanas funkcijai (un kad tās nav)
Tā kā bultiņas funkcijas tuvojas this, tie ir lieliski piemēroti daudziem atzvanīšanas scenārijiem, kuros vēlaties, lai atzvanīšana turpinātu atsaukties uz apkārtējo objektu vai kontekstu. Tas ir īpaši ērti ar taimeriem, solījumiem un masīvu metodēm, piemēram, map, filter, un reduce.
Iedomājieties metodi, kurai atkārtoti jāatjaunina kāds īpašums, izmantojot setInterval. Izmantojot tradicionālu funkciju, this atzvanīšanas ietvaros pēc noklusējuma tiktu izmantots globālais objekts (vai arī undefined stingrā režīmā), tātad this.count nenorādītu uz jūsu instanci. Izmantojot bultiņas funkciju, atzvanīšanas funkcija dabiski izmanto this ārējās metodes.
function Counter() {
this.count = 0;
setInterval(() => {
this.count++;
}, 1000);
}
Pateicoties bultiņai, this Intervāla iekšpusē atzvanīšanas funkcija attiecas uz Counter piemēram, nevis window. Ja šī atzvanīšanas funkcija būtu parasta funkcija, jums būtu nepieciešams vai nu .bind(this) vai starpposma mainīgais, piemēram const self = this; lai saglabātu atsauci.
Bultu funkcijas arī vienkāršo kodu, izmantojot masīvu metodes, kur bieži vien nerūp this vispār. Kad jūs nododat tradicionālu funkciju kā atzvanīšanas funkciju, netiešā this parasti ir undefined, un jūs to varētu aizmirst. Bultiņas vizuāli parāda, ka funkcija ir tikai tīra ieejas datu kartēšana ar izejas datiem.
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
Tomēr ir svarīgi gadījumi, kad bultiņu funkcijas nav pareiza izvēle, īpaši, ja nepieciešama dinamiska this. Divi klasiski anti-modeļu piemēri izmanto bultiņu funkcijas kā objektu metodes un kā DOM notikumu apstrādātājus, kas paļaujas uz this būdams elements.
Apsveriet objektu, kas izseko kaķa dzīvi:
const cat = {
lives: 9,
jump: () => {
this.lives--; // bug: this is not cat
},
};
cat.jump();
Kopš jump ir bulta, this neatsaucas uz cat bet lai nu kā this bija vieta, kur tika izveidots objekta literālis (bieži vien globālais objekts). Paredzētais this.lives-- vai nu met (stingrā režīmā), vai klusi mutē kaut ko nesaistītu. Šeit pareiza ir parastas metodes sintakses izmantošana.
DOM notikumu klausītāji ir līdzīgi: standarta modelis this.classList.toggle('on') notikuma iekšienē atzvanīšanas funkcija balstās uz this būdams elements, kas aizsāka notikumu. Ar bultiņas funkciju, this vairs nenorāda uz elementu, tāpēc kods nedarbojas.
const button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on'); // this is not button
});
Šādā situācijā apstrādātājam jābūt parastai funkcijai, lai this pārlūkprogramma saista ar pogas elementu. Bultiņu funkcijas vienkārši nedarbojas kā aizvietotāji, ja jūsu loģika sagaida this būt par dinamisko notikuma mērķi.
Vēl viens neliels trūkums ir tas, ka bultiņu funkcijas ir sintaktiski anonīmas. Parasti tiem nav sava nosaukuma (izņemot jebkuru mainīgo, kuram tie ir piešķirti), kas var padarīt kaudzes pēdas nedaudz mazāk aprakstošas un rekursiju nedaudz sarežģītāku. Lielākajā daļā reālās pasaules koda tas ir pārvaldāms kompromiss, taču ir vērts to atcerēties.
Īpaši gadījumi: geteri, seteri, saistītās metodes un nepāra stūri
Getteri un seteri ievēro vienu un to pašu “izsaukuma vietnes” noteikumu: this ir objekts, kurā tiek piekļūts īpašībai, nevis tas, kurā tā sākotnēji tika definēta. Ja getter tiek mantots no prototipa un jūs to izsaucat atvasinātā objektā, this getter iekšpusē attiecas uz atvasināto objektu.
Saistītās metodes, kas izveidotas ar Function.prototype.bind sniedz jums darbību, kas ir nedaudz līdzīga bultiņu funkcijām, bet parasto funkciju līmenī. Kad tu zvani f.bind(obj), jūs izveidojat jaunu funkciju, kuras this ir pastāvīgi fiksēts obj, neatkarīgi no tā, kā tas tiek izsaukts. Tas var būt noderīgi klasēs, kad ir nepieciešams saglabāt this pat ja metode ir atdalīta.
class Example {
constructor() {
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this); // always the instance
}
}
Gan saistīto metožu, gan bultiņu funkciju, ko izmanto kā instanču laukus, trūkums ir tāds, ka katrs eksemplārs saņem savu funkcijas kopiju, kas var palielināt atmiņas izmantošanu. Šis kompromiss parasti ir pieņemams, ja saistat tikai nelielu skaitu bieži atvienotu metožu, taču tas ir jāņem vērā veiktspējai kritiskā kodā.
Ir arī daži mantojuma stūra gadījumi, kuros this uzvedas atšķirīgi, piemēram, novecojušā with paziņojums, apgalvojums. Iekšpusē a with (obj) { ... } bloks, izsaucot funkciju, kas ir īpašība obj efektīvi uzvedas tā, it kā jūs būtu rakstījis obj.method(), Tik this ir saistīts ar objMūsdienu kodam vajadzētu izvairīties no with, taču šī izņēmuma izpratne paskaidro, ka this joprojām būtībā ir atkarīgs no tā, kā tiek veidots funkcijas izsaukums.
Arī HTML iekļautajiem notikumu apstrādātājiem ir īpašs noteikums: apkārtējais iekļautā apstrādātāja kods redz this kā elements, bet iekšējās funkcijas, kas definētas šajā apstrādātājā, atgriežas pie regulārās this noteikumiem. Tātad iekšēja tradicionāla funkcija, kas nav saistīta ne ar ko, parasti redzēs this as globalThis (Vai undefined stingrajā režīmā), nevis elements.
Visbeidzot, atcerieties, ka bultiņu funkcijām nav prototype īpašums un tos nevar izmantot kā konstruktorus ar new. Mēģinājums new MyArrow() metīs TypeError. Ja nepieciešama funkcija, kas var darboties kā konstruktors, jāizmanto parasta funkcija vai klase.
Paturot prātā šīs detaļas, ir daudz vieglāk izvēlēties starp bultiņu funkcijām un tradicionālajām funkcijām. Izmantojiet bultiņas tur, kur vēlaties leksikālu this un kodolīgu sintaksi, un atgriezties pie parastajām funkcijām, kad vien nepieciešama dinamiskā, izsaukuma vietnes vadītā this uzvedība vai konstruktora semantika.
Kad esat internalizējis, kā this ir saistīts katrā situācijā, bultiņu funkcijas kļūst par spēcīgu sabiedroto, nevis pārsteidzošu kļūdu avotu. Tie vienkāršo tādus izplatītus modeļus kā atzvanīšanas funkcijas un vienkāršas transformācijas, savukārt parastās funkcijas turpina apstrādāt lomas, kas ir atkarīgas no to pašu funkcijām. this saistīšana, piemēram, metodes, konstruktori un dinamiskie notikumu apstrādātāji.