Funkcie šípka a `this` v JavaScripte: kompletný sprievodca

  • Hodnota this V JavaScripte to závisí od spôsobu volania funkcie, pričom sa líši medzi globálnym kontextom, objektovými metódami a použitím striktného režimu.
  • Šípkové funkcie si nevytvárajú vlastné thisNamiesto toho lexikálne dedia terminológiu domény, v ktorej boli definované, čo zabraňuje mnohým problémom pri spätných volaniach.
  • Odporúča sa používať šípkové funkcie vo volaniach a metódach polí, ale vyhýbajte sa im ako objektovým metódam, konštruktorom alebo obslužným programom udalostí DOM, ktoré vyžadujú this dynamický.

Ilustrácia šípky a tejto funkcie v JavaScripte

Ak už nejaký čas programujete v JavaScripte, určite rozpoznáte kľúčové slovo Toto ti spôsobilo viac ako jednu bolesť hlavyA odkedy sa v ES6 objavili šípkové funkcie, veci sa ešte viac skomplikovali... alebo zjednodušili, v závislosti od toho, ako sa na to pozeráte.

V tomto článku sa bližšie pozrieme na to, ako to funguje Toto platí pre tradičné funkcie a funkcie so šípkami.Prečo sa niekedy zdá, že ukazuje na konkrétny objekt a inokedy na globálny objekt, a v ktorých situáciách má zmysel používať šípkové funkcie a v ktorých situáciách je lepšie sa im vyhnúť?

Presne tak this v JavaScripte

Vyhradené slovo this Je to odkaz na kontext vykonávania funkcie, ktorá sa práve vykonáva. Na rozdiel od iných jazykov, v JavaScripte rozhodnutie nie je založené na tom, kde je funkcia definovaná, ale skôr na jej vykonaní. ako vyvolať.

To znamená, že tá istá funkcia môže byť volaná rôznymi spôsobmi a v každom z nich, this môže ukazovať na iný objektNie je to niečo, čo môžete zmeniť priamym zadaním (nemôžete to urobiť this = algo), ale môžete to ovplyvniť špecifickými mechanizmami, ako napríklad call, apply y bind.

Okrem toho sa ich správanie líši medzi prísny režim a neprísny režimV nestriktnom režime, ak zavoláte funkciu „bare“ (bez objektu pred ňou), this Zvyčajne ide o globálny objekt (v prehliadači, window), zatiaľ čo v prísnom režime to môže byť undefinedToto rozlíšenie je dôležité pri porovnávaní príkladov kódu z rôznych zdrojov.

Toto v globálnom kontexte a pri bežných funkciách

V prehliadačoch, keď sa nenachádzate v žiadnom module alebo funkcii, globálnym kontextom je objekt window, a tam this ukážte na daný objektTeda, ak do konzoly zadáte nasledovné:

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

V rámci funkcie deklarovanej „klasickým“ spôsobom (normálna funkcia) je hodnota this Záleží na tom, ako sa tá funkcia nazýva.Ak ho vyvoláte bez predchádzajúceho objektu, v nestriktnom režime this Zvyčajne ide o globálny a prísne vzaté, bude undefinedPreto niekedy pri presúvaní kódu z jednej stránky na druhú, Toto už nie je to, čo ste očakávali..

Toto v objektových metódach definovaných s normálnymi funkciami

Keď definujete metódu na objekte pomocou tradičnej syntaxe, this v rámci metódy, odkaz na samotný objekt z ktorého bola daná metóda vyvolaná.

Napríklad, ak máte niečo ako:

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

Hovor obj.speak() robí this vnútri speak buď ten pravý objToto je správanie, ktoré ľudia zvyčajne intuitívne očakávajú: metóda hovorí „v mene“ objektu.

Ak použijete klasickú funkciu namiesto skrátenej syntaxe, efekt je rovnaký, pretože Kľúčom je spôsob, akým sa metóda vyvolávaNezáleží na tom, či ste použili skratku metódy alebo kľúčové slovo function vo vnútri objektu.

Toto v metódach definovaných pomocou šípkových funkcií

Veci sa zmenia, keď definujete metódu pomocou šípkovej funkcie. Niečo ako:

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

V tomto prípade, pri vykonávaní obj2.speak() to uvidíš this už nie je obj2ale vonkajší lexikálny kontext k tomuto objektu, ktorý je v klasickom skripte prehliadača zvyčajne globálnym objektom window.

Na prvý pohľad je to záhadné, pretože očakávate, že metóda objektu bude ukazovať na samotný objekt. Avšak šípkové funkcie si nevytvárajú vlastné thisZdedia hodnotu this rozsahu, v ktorom boli definované. Ak je tento rozsah globálny, zdedia globálny rozsah; ak je iný, zdedia tento iný rozsah.

Preto sa v modernej dokumentácii často opakuje odporúčanie: Nepoužívajte šípkové funkcie ako metódy objektu keď potrebuješ this zamerajte sa na daný objekt.

Lexikálny rozsah this funkcie šípok

Kľúčový rozdiel medzi normálnymi funkciami a šípkovými funkciami spočíva v tom, že tie druhé majú lexikálne prepojenie pre thisJednoducho povedané: oni o svojom nerozhodujú this nie keď si volajú, ale keď si vytvoriť.

Predstavte si tento príklad:

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

Tu by sa mohlo zdať, že ako vo vnútri speak vykonáme funkciu so šípkou, Toto by sa malo „resetovať“ na globálneAle stane sa presný opak: funkcia šípky zachytáva this funkcie, ktorá ho obklopuječo je v tomto prípade metóda speak vyvolané ako obj3.speak()Preto hodnota this Ten, ktorý je zobrazený na konzole, je ten z obj3.

Chcem tým povedať, funkcie šípok nemajú vlastné thisale radšej znovu využijú to, čo majú z bezprostredného okoliaToto je neuveriteľne užitočné vo vnorených spätných volaniach, časovačoch, promise a kdekoľvek inde, kde ste sa pri klasických funkciách museli potýkať s... .bind alebo s trikmi ako napríklad const that = this;.

Praktické príklady straty a zachovania this

Jedným z klasických problémov v JavaScripte je, že pri definovaní funkcie vo vnútri metódy, stratíš odkaz na this ktorý ukazoval na objekt a skončíte s tým globálnym alebo s undefined.

Zoberme si typický prípad použitia setTimeout v rámci metódy objektu s tradičnou funkciou:

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

Tu toto v rámci funkcie odovzdanej setTimeout Už to nie je objekt personaTáto funkcia spätného volania sa vykoná v globálnom kontexte (v prehliadači, window), takže this.nombre Snaží sa prečítať vlastnosť v globálnom premennom, ktorá neexistuje, a nakoniec je undefined.

Predtým, ako existovali šípkové funkcie, bežným riešením bolo ukladanie hodnoty this v pomocnej premennej "presunúť" ho do funkcie:

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

Vďaka tejto premennej sa zachová správny odkaz na objekt. Je to však trochu nepekný a opakujúci sa trik. S funkciami so šípkami je tento problém značne zjednodušený:

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

Tu funkcia šípky nevytvára svoju vlastnú this, tak zdedí this metódy decirNombrektorý je objektom personaVýsledok: „Agustin“ sa zobrazuje správne bez potreby medziľahlých premenných alebo .bind.

volanie, použitie a vytvorenie väzby: riadenie hodnoty this

Okrem „prirodzeného“ spôsobu nastavenia kontextu volaním metódy nám JavaScript poskytuje nástroje na vynútiť hodnotu this pri normálnych funkciách: call, apply y bind.

Metódy call() y apply() Funkciu volajú okamžite, čo vám umožňuje odovzdať objekt, ktorý chcete použiť ako this. Rozdiel je v tom call prijíma argumenty jeden po druhom, zatiaľ čo apply Sú prijímané v poli. bind(), namiesto toho vráti novú funkciu s this „pripojené“ k hodnote, ktorú ste uviedliaby si jej mohol zavolať neskôr, keď ti to bude vyhovovať.

Pri šípkových funkciách však tieto metódy nie sú užitočné na zmenu this pretože jeho hodnota je lexikálne prepojená. Môžeš použiť call, apply o bind odovzdávať argumenty, ale nie meniť kontext šípkových funkcií, čo je veľmi dôležitý rozdiel oproti bežným funkciám.

Základná syntax šípkových funkcií

Okrem správania thisFunkcie šípok poskytujú kompaktnejšia a expresívnejšia syntax pre mnoho situácií. Všeobecný tvar je:

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

Táto forma automaticky vráti výsledok výrazu napravo od šípky, takže Nie je potrebné písať slovo return keď máte iba jeden jednoduchý výraz.

Niektoré bežné syntaktické body:

  • Bez parametrov:
    () => 42 alebo dokonca _ => 42 ak ti nezáleží na názve argumentu.
  • S jedným parametrom:
    Zátvorky sú voliteľné; môžete napísať x => x * 2 o (x) => x * 2.
  • S viacerými parametrami:
    Zátvorky sú povinné: (x, y) => x + y.

Ak potrebujete viacero príkazov, môžete použiť telo bloku s kľúčmi:

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

V tomto prípade, keďže existujú kľúče, Už neexistuje žiadny implicitný návrat; ak nedáš returnfunkcia vráti undefinedToto platí pre funkcie so šípkami aj pre tradičné funkcie.

Vrátiť literálne objekty pomocou šípkových funkcií

Existuje malý, ale veľmi bežný syntaktický detail: keď šípková funkcia vráti doslovný objekt priamoMusíte ho uzavrieť do zátvorky, aby si ho interpret nepomýlil s blokom.

Napríklad:

x => ({ y: x })

Bez týchto zátvoriek by JavaScript interpretoval zložené zátvorky ako začiatok tela funkcie, nie ako objekt. Je to jednoduchý trik, ale ak naň zabudnete, spôsobí veľa hlúpych chýb.

Šípkové funkcie: anonymné a bez prototypu

Funkcie šípok sú syntakticky anonymnýNemajú vlastné mená, čo môže veci trochu komplikovať. ladiace a chybové hláseniapretože v sledovaní nevidíte priamo identifikátor funkcie, pokiaľ ste ho nepriradili konštante s rozpoznateľným názvom.

Okrem toho, funkcie šípok Nevlastnia majetok prototype a nemôžu byť použité ako stavebné spoločnostiAk sa ich pokúsite privolať newDostanete chybu. Na vytvorenie objektov pomocou konštruktorov alebo tried musíte stále použiť bežné funkcie alebo syntax. class.

Ďalším dôsledkom je, že Nie sú vhodné pre vzory, ktoré vyžadujú interné samoreferencovanie., ako napríklad niektoré formy rekurzie alebo obslužné rutiny udalostí, ktoré sa musia odhlásiť pomocou this alebo vlastný názov funkcie.

Kde vynikajú funkcie šípok

Veľkou silou šípkových funkcií je práve ich lexikálne prepojenie thisSú ideálne v situáciách, keď chcete, aby spätné volanie, ktoré odovzdávate inej funkcii, zachovalo this okolitého okolia.

Napríklad v objekte s metódou, ktorá spúšťa časovač a potrebuje k nemu neustále pristupovať vlastnosti samotného objektu pomocou this:

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

V ES5 bolo bežné, že bolo potrebné dať .bind(this) na spätné volanie alebo uložiť this v inej premennej. Pri šípkových funkciách, Kód sa stáva čistejším a bližším skutočnému zámeru..

Sú tiež veľmi praktické s metódami polí, ako napríklad map, filter, reduce a spoločnosť, pretože znížiť syntaktický šum keď je logika funkcie krátka:

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

Pri striedmom použití tieto kompaktné tvary uľahčujú sledovanie toku údajov na prvý pohľad.

Kedy sa vyhnúť šípkovým funkciám

Hoci sú šípkové funkcie veľmi užitočné, nenahrádzajú bežné funkcie. Existuje niekoľko jasných prípadov, kedy Najlepšie je ich nepoužívať.:

  • Metódy objektov, ktoré závisia od this:
    Ak definujete metódu ako saltos: () => { this.vidas--; } vo vnútri objektu gato, this Nebude ukazovať na mačku, ale na vonkajšie prostredie a vlastnosť sa neaktualizuje podľa očakávaní.
  • Spätné volania udalostí DOM, ktoré potrebujú this dynamický:
    V obslužnom programe ako boton.addEventListener('click', () => { this.classList.toggle('on'); });, this Nebude to stlačené tlačidlo, ale vyšší kontext, ktorý vám pravdepodobne zobrazí chybu typu.
  • Tvorcovia alebo funkcie, ktoré potrebujú prototype:
    Keďže sa nedá použiť s newŠípkové funkcie nie sú vhodné na vytváranie inštancií alebo pre vzory založené na prototypoch.

Vo všetkých týchto prípadoch, a Normálna funkcia zostáva vhodným nástrojom pretože to umožňuje this Je dynamicky prepojený so spôsobom, akým voláte funkciu.

Ak si zvyknete vedome vyberať medzi normálnou funkciou a funkciou šípky v závislosti od toho, čo od nej potrebujete this a z kontextu, Váš kód bude predvídateľnejší a čitateľnejší. pre každého, kto si to potom ponechá.

Nakoniec, pochopenie toho, ako sa hodnota this v JavaScripte a ako funkcie šípok dedia túto hodnotu Kľúčom k tomu, ako sa vyhnúť problémom s neočakávanými výsledkami, využiť syntaktický cukor ES6 a napísať metódy, spätné volania a obslužné rutiny udalostí, ktoré robia presne to, čo ste mali na mysli, je pochopenie lexikálneho rozsahu, v ktorom sú vytvorené.