Funkce Šipka a `this` v JavaScriptu: kompletní průvodce

  • Hodnota this V JavaScriptu záleží na způsobu volání funkce, a to v globálním kontextu, objektových metodách a použití striktního režimu.
  • Funkce se šipkami si nevytvářejí vlastní thisMísto toho lexikálně dědí terminologii domény, ve které byly definovány, což zabraňuje mnoha problémům při zpětných voláních.
  • Doporučuje se používat šipkové funkce ve zpětných voláních a metodách polí, ale vyhněte se jim jako objektovým metodám, konstruktorům nebo obslužným rutinám událostí DOM, které vyžadují this dynamický.

Ilustrace šipky a této funkce v JavaScriptu

Pokud už nějakou dobu programujete v JavaScriptu, jistě rozpoznáte klíčové slovo Tohle ti způsobilo nejednu bolest hlavyA od té doby, co se v ES6 objevily šipkové funkce, se věci ještě více zkomplikovaly... nebo zjednodušily, záleží na tom, jak se na to díváte.

V tomto článku se blíže podíváme na to, jak to funguje To platí pro tradiční funkce a funkce se šipkami.Proč se někdy zdá, že ukazuje na konkrétní objekt a jindy na globální objekt, a v jakých situacích má smysl používat šipkové funkce a v jakých je lepší se jim vyhnout?

Qué es exclusiveamente this v JavaScriptu

Vyhrazené slovo this Je to odkaz na kontext spuštění funkce, která se aktuálně provádí. Na rozdíl od jiných jazyků se v JavaScriptu rozhodování nezakládá na tom, kde je funkce definována, ale spíše na jejím provedení. jak vyvolat.

To znamená, že stejnou funkci lze volat různými způsoby a v každém z nich, this může ukazovat na jiný objektNení to něco, co můžete změnit přímým úkolem (nemůžete to udělat this = algo), ale můžete to ovlivnit specifickými mechanismy, jako například call, apply y bind.

Navíc se jejich chování liší mezi striktní režim a nestriktní režimV nestriktním režimu, pokud zavoláte funkci „bare“ (bez objektu před ní), this Obvykle se jedná o globální objekt (v prohlížeči, window), zatímco v striktním režimu to může být undefinedToto rozlišení je důležité při porovnávání příkladů kódu z různých zdrojů.

To v globálním kontextu a v běžných funkcích

V prohlížečích, když se nenacházíte uvnitř žádného modulu nebo funkce, je globálním kontextem objekt windowa tam this ukažte na daný objektTedy pokud do konzole zadáte následující:

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

V rámci funkce deklarované „klasickým“ způsobem (normální funkce) je hodnota this Záleží na tom, jak se ta funkce nazývá.Pokud jej vyvoláte bez předchozího objektu v nestriktním režimu this Obvykle se jedná o globální a přesně vzato bude undefinedProto někdy, při přesouvání kódu z jednoho webu na druhý, Tohle už jsi nečekal/a..

Toto v objektových metodách definovaných s normálními funkcemi

Když definujete metodu objektu pomocí tradiční syntaxe, this v rámci metody, odkaz na samotný objekt ze kterého byla daná metoda vyvolána.

Například, pokud máte něco jako:

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

Volání obj.speak() dělá this v speak buď ten pravý objToto je chování, které lidé obvykle intuitivně očekávají: metoda hovoří „jménem“ objektu.

Pokud použijete klasickou funkci místo zkrácené syntaxe, efekt je stejný, protože Klíč spočívá ve způsobu, jakým je metoda volánaNezáleží na tom, zda jste použili zkratku metody nebo klíčové slovo. function uvnitř objektu.

Toto v metodách definovaných pomocí šipkových funkcí

Věci se změní, když definujete metodu pomocí funkce typu šipka. Něco jako:

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

V tomto případě, při provádění obj2.speak() to uvidíš this Už to není obj2ale vnější lexikální kontext k tomuto objektu, což je v klasickém prohlížečovém skriptu obvykle globální objekt window.

Napoprvé je to matoucí, protože očekáváte, že metoda objektu bude ukazovat na samotný objekt. Nicméně... Funkce se šipkami si nevytvářejí vlastní thisZdědí hodnotu this rozsahu, ve kterém byly definovány. Pokud je tento rozsah globální, zdědí tento globální rozsah; pokud se jedná o jiný, zdědí tento jiný rozsah.

Proto se v moderní dokumentaci často opakuje doporučení: Nepoužívejte šipkové funkce jako objektové metody. když potřebujete this zaměřit se na daný objekt.

Lexikální rozsah this funkce šipek

Klíčový rozdíl mezi normálními funkcemi a šipkovými funkcemi spočívá v tom, že ty druhé mají lexikální vazbu pro thisJednoduše řečeno: oni o sobě nerozhodují this ne když si volají, ale když si vytvořit.

Představte si tento příklad:

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

Zde by se mohlo zdát, že stejně jako uvnitř speak provedeme funkci se šipkou, Toto by se mělo „resetovat“ na globálníAle děje se pravý opak: funkce šipky zachycuje this funkce, která ji obklopujecož je v tomto případě metoda speak vyvoláno jako obj3.speak()Proto hodnota this Ten, který je zobrazen na konzoli, je ten z obj3.

To znamená, že Funkce šipek nemají své vlastní thisale spíše znovu využívají to, co mají z bezprostředního okolíTo je neuvěřitelně užitečné u vnořených zpětných volání, časovačů, promisů a kdekoli jinde, kde jste se u klasických funkcí museli potýkat s... .bind nebo s triky jako const that = this;.

Praktické příklady ztráty a zachování this

Jedním z klasických problémů v JavaScriptu je, že při definování funkce uvnitř metody ztrácíš odkaz na this který ukazoval na objekt a skončíte s tím globálním nebo s undefined.

Vezměme si typický případ použití setTimeout v rámci metody objektu s tradiční funkcí:

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

Zde toto v rámci funkce předané setTimeout Už to není objekt personaTato callback funkce se provádí v globálním kontextu (v prohlížeči, window), tak this.nombre Snaží se přečíst vlastnost v globálním proměnném, která neexistuje, a nakonec je undefined.

Než existovaly šipkové funkce, běžným řešením bylo ukládání hodnoty this v pomocné proměnné "přetáhnout" ho do funkce:

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

Díky této proměnné se zachovává správný odkaz na objekt. Je to ale poněkud ošklivý a opakující se trik. U šipkových funkcí je tento problém značně zjednodušený:

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

Zde funkce šipky nevytváří vlastní thisano zdědí this metody decirNombrekterý je objektem personaVýsledek: „Agustin“ se zobrazuje správně bez nutnosti mezilehlých proměnných nebo .bind.

volání, použití a navázání: řízení hodnoty this

Kromě „přirozeného“ způsobu nastavení kontextu voláním metody nám JavaScript poskytuje nástroje pro vynutit hodnotu this v normálních funkcích: call, apply y bind.

Metody call() y apply() Okamžitě volají funkci, což vám umožňuje předat objekt, který chcete použít jako this. Rozdíl je v tom call přijímá argumenty jeden po druhém, zatímco apply Jsou přijímány v poli. bind(), místo toho vrací novou funkci s this „připojeno“ k vámi uvedené hodnotětakže jí můžeš zavolat později, až ti to bude vyhovovat.

U šipkových funkcí však tyto metody nejsou užitečné pro změnu this protože jeho hodnota je lexikálně propojena. Můžeš použít call, apply o bind předávat argumenty, ale ne modifikovat kontext šipkových funkcí, což je velmi důležitý rozdíl oproti běžným funkcím.

Základní syntaxe šipkových funkcí

Kromě chování thisFunkce šipek poskytují kompaktnější a expresivnější syntax pro mnoho situací. Obecný tvar je:

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

Tato forma automaticky vrací výsledek výrazu napravo od šipky, takže Není třeba psát slovo return když máte pouze jeden jednoduchý výraz.

Některé společné syntaktické body:

  • Bez parametrů:
    () => 42 nebo dokonce _ => 42 pokud vám nezáleží na názvu argumentu.
  • S jediným parametrem:
    Závorky jsou volitelné; můžete napsat x => x * 2 o (x) => x * 2.
  • S více parametry:
    Závorky jsou povinné: (x, y) => x + y.

Pokud potřebujete více příkazů, můžete použít tělo bloku s klíči:

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

V tomto případě, protože existují klíče, Již neexistuje žádný implicitní návrat; pokud nevložíte returnfunkce vrátí undefinedTo platí jak pro šipkové funkce, tak pro tradiční funkce.

Vraťte literální objekty pomocí funkcí se šipkami

Existuje malý, ale velmi častý syntaktický detail: když šipková funkce vrací doslovný objekt přímoMusíte ho uzavřít do závorek, aby si ho interpret nezaměnil s blokem.

Například:

x => ({ y: x })

Bez těchto závorek by JavaScript interpretoval složené závorky jako začátek těla funkce, nikoli jako objekt. Je to jednoduchý trik, ale pokud na něj zapomenete, způsobí spoustu hloupých chyb.

Funkce šipky: anonymní a bez prototypu

Funkce šipek jsou syntakticky anonymníNemají vlastní jména, což může věci poněkud komplikovat. ladicí a chybové zprávyprotože ve stopě nevidíte identifikátor funkce přímo, pokud jste jej nepřiřadili konstantě s rozpoznatelným názvem.

Funkce šipek navíc Nevlastní majetek prototype a nemohou být použity jako stavební firmyPokud se je pokusíte přivolat pomocí newDostanete chybu. Chcete-li vytvářet objekty pomocí konstruktorů nebo tříd, stále musíte použít běžné funkce nebo syntaxi. class.

Dalším důsledkem je, že Nejsou vhodné pro vzory, které vyžadují interní sebereferencování., například některé formy rekurze nebo obslužné rutiny událostí, které se musí odhlásit pomocí this nebo vlastní název funkce.

Kde funkce šipek vynikají

Velká síla šipkových funkcí spočívá právě v jejich lexikální propojení thisJsou ideální v situacích, kdy chcete, aby zpětné volání předávané jiné funkci zachovalo this okolní oblasti.

Například v objektu s metodou, která spouští časovač a potřebuje k němu neustále přistupovat vlastnosti samotného objektu pomocí this:

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

V ES5 bylo běžné, že bylo nutné uvést .bind(this) na zpětné volání nebo uložit this v jiné proměnné. S šipkovými funkcemi, Kód se stává čistším a blíže skutečnému záměru..

Jsou také velmi praktické s metodami pole, jako je například map, filter, reduce a společnost, protože snížit syntaktický šum když je logika funkce krátká:

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

Při střídmém použití tyto kompaktní tvary usnadňují sledování toku dat na první pohled.

Kdy se vyhnout funkcím šipek

Přestože jsou šipkové funkce velmi užitečné, nenahrazují běžné funkce. Existuje několik jasných případů, kdy Nejlepší je je nepoužívat.:

  • Metody objektů, které závisí na this:
    Pokud definujete metodu jako saltos: () => { this.vidas--; } uvnitř objektu gato, this Nebude ukazovat na kočku, ale na vnější prostředí a vlastnost se neaktualizuje podle očekávání.
  • Zpětná volání událostí DOM, která vyžadují this dynamický:
    V obslužném programu jako boton.addEventListener('click', () => { this.classList.toggle('on'); });, this Nebude to stisknuté tlačítko, ale vyšší kontext, který vám pravděpodobně způsobí chybu typu.
  • Tvůrci nebo funkce, které potřebují prototype:
    Protože jej nelze použít s newFunkce se šipkami nejsou vhodné pro vytváření instancí ani pro vzory založené na prototypech.

Ve všech těchto případech Normální funkce zůstává vhodným nástrojem protože to umožňuje this Je dynamicky propojen se způsobem, jakým funkci voláte.

Pokud si zvyknete vědomě volit mezi normální funkcí a funkcí šipky v závislosti na tom, co od this a z kontextu, Váš kód bude předvídatelnější a čitelnější. pro každého, kdo si to potom ponechá.

V konečném důsledku, pochopení toho, jaká je hodnota this v JavaScriptu a jak šipkové funkce dědí tuto hodnotu Klíčem k tomu, abyste se vyhnuli potížím s neočekávanými výsledky, využili syntaktický cukr ES6 a napsali metody, zpětná volání a obslužné rutiny událostí, které dělají přesně to, co jste měli na mysli, je pochopení lexikálního rozsahu, ve kterém jsou vytvořeny.