
Ako programirate u JavaScriptu, prije ili kasnije ćete naići na funkcije strelica i zloglasni `this` , i često uopće nije jasno šta se događa ispod. Između klasičnih funkcija, metoda, povratnih poziva i objekata, lako se zbuniti kontekstom i završiti otklanjajući grešaka u nečemu što satima izgleda "magično".
U sljedećim redovima, razotkrit ćemo svu tu "magiju" i vidjeti, smireno, ali direktno, kako funkcioniraju funkcije strelica, kako se razlikuju od regularnih funkcija i kakav je njihov stvarni odnos s `ovim` . Također ćemo pogledati ulogu metoda i funkcija općenito kako biste imali potpun pregled i mogli pisati moderan, čist i održiv kod.
Osnove: Šta je funkcija u JavaScriptu?
U JavaScriptu, funkcija je u suštini blok koda koji se može ponovo koristiti, definisan za izvršavanje određenog zadatka . Nakon što je deklarišete, možete je pozivati onoliko puta koliko želite, sa različitim ulaznim vrijednostima, bez ponavljanja istog koda iznova i iznova.
Kada definirate klasičnu funkciju, koristite ključnu riječ ` function` , opcionalno ime i listu parametara u zagradama. Parametri predstavljaju ulaze, a povratna vrijednost predstavlja izlaz . Nešto tipično poput sabiranja dva broja izgleda ovako:
function suma(a, b) {
return a + b;
}
let resultado = suma(5, 3);
console.log(resultado); // 8
U ovom dijelu koda, funkcija sum prima dva parametra, a i b , izvršava internu operaciju i vraća rezultat pomoću funkcije return , koju zatim možemo sačuvati u varijabli ili direktno koristiti.
Funkcije također mogu imati parametre sa zadanim vrijednostima , što je vrlo korisno za izbjegavanje grešaka kada kod poziva funkciju bez svih očekivanih argumenata:
function saludo(nombre = "desconocido") {
return `¡Hola, ${nombre}!`;
}
console.log(saludo()); // ¡Hola, desconocido!
console.log(saludo("Alice")); // ¡Hola, Alice!
Zahvaljujući zadanim parametrima, vaša funkcija postaje robusnija i tolerantnija na nepotpune pozive , što vam štedi ručne provjere unutar tijela funkcije.
Pored sintakse, mudro korištenje funkcija donosi modularnost, mogućnost ponovne upotrebe i poboljšanu čitljivost : veliki problem se rastavlja na male, lako razumljive, lako testirane i održavajuće dijelove. Mnogo je jednostavnije testirati izoliranu funkciju nego ogroman blok koda pomiješan sa svime ostalim.
Normalne funkcije, metode i njihov kontekst
Prije nego što se udubimo u funkcije sa strelicama, korisno je razumjeti kako se tradicionalne funkcije ponašaju u odnosu na `this` kontekst i koja je razlika između "labavih" funkcija i metoda povezanih s objektima.
Regularna funkcija deklarirana sa `function` ima dinamičko ponašanje `this` : vrijednost `this` ovisi o tome kako se funkcija poziva, a ne gdje je napisana u kodu. Ako je pozovete kao samostalnu funkciju, `this` može pokazivati na globalni objekt (u nestriktnom načinu rada) ili biti nedefinirano (u striktnom načinu rada). Ako je pozovete kao metodu objekta, `this` se obično odnosi na taj objekt.
Kada je ta funkcija dio objekta, obično je nazivamo metodom . Metoda je jednostavno funkcija pohranjena u svojstvu objekta. U tom kontekstu, `this` obično ukazuje na sam objekt kada pozivamo metodu koristeći tačkastu notaciju:
const persona = {
nombre: "Laura",
saludar: function () {
console.log("Hola, soy " + this.nombre);
}
};
persona.saludar(); // "Hola, soy Laura" (this => persona)
Ključna razlika između funkcije i metode nije tehničke prirode (obje su funkcije "ispod haube"), već gdje su pohranjene i kako se pozivaju . "Labave" funkcije se pozivaju po imenu, dok se metode pozivaju kroz objekt, a to direktno utiče na vrijednost `this`.
Osim toga, normalne funkcije se mogu koristiti kao konstruktori sa new , koji kreira instance objekata sa zajedničkim prototipom, a imaju i objekat arguments koji prikuplja sve proslijeđene argumente, čak i ako nisu eksplicitno definirani kao parametri.
Funkcije strelica: moderna sintaksa
Sa ECMAScript 6 (ES6) došle su i funkcije sa strelicama , koje nude mnogo koncizniji način pisanja funkcija, posebno kada su kratke ili se koriste kao povratne funkcije.
Najjednostavniji oblik streličaste funkcije zamjenjuje riječ function sa arrow => i u mnogim slučajevima omogućava implicitni povratak, također čuvajući povratak i vitičaste zagrade kada je tijelo funkcije jedan izraz:
const multiplicacion = (x, y) => x * y;
console.log(multiplicacion(4, 5)); // 20
Ove vrste funkcija se obično deklarišu kao izrazi pohranjeni u konstantama ili varijablama , jer su po svojoj prirodi anonimne (nemaju vlastito ime kao klasične deklarisane funkcije) i ne koriste se kao deklaracije funkcija s povišenim nivoima.
Opšta sintaksa se može sažeti na sljedeći način: parametri u zagradama, strelica =>, a zatim izraz ili blok koda . Ako imate samo jedan parametar, možete izostaviti zagrade; ako imate više parametara, oni su obavezni.
// Un solo parámetro, sin paréntesis
const doble = n => n * 2;
// Varios parámetros, paréntesis obligatorios
const sumar = (a, b) => a + b;
Kada logika više ne stane u jedan izraz i potrebno vam je nekoliko naredbi, koristite vitičaste zagrade i eksplicitni return , baš kao u tradicionalnoj funkciji:
const sumaCompleja = (a, b) => {
let resultado = a + b;
resultado = resultado * 2;
return resultado;
};
Funkcije strelica također prihvataju parametre sa zadanim vrijednostima , što vam omogućava da definirate jasne početne vrijednosti bez potrebe za dodatnim internim provjerama:
const cuadrado = (lado = 10) => lado * lado;
console.log(cuadrado()); // 100
console.log(cuadrado(5)); // 25
Budući da funkcionišu kao izrazi, uobičajeno je da se nekoliko funkcija strelica pohrani u različite varijable za kasniju ponovnu upotrebu, na primjer:
const duplicar = n => n * 2;
const triplicar = n => n * 3;
console.log(duplicar(3)); // 6
console.log(triplicar(3)); // 9
Implicitni povratak i praktična sintaksa
Jedna od velikih prednosti streličnih funkcija je implicitni povratak kada se tijelo svodi na jedan izraz . Ovo čini vrlo uobičajene zadatke, poput transformiranja vrijednosti, izuzetno čistim:
const duplicar = n => n * 2;
Ovaj primjer je funkcionalno ekvivalentan pisanju normalne funkcije koja koristi return unutar bloka , ali štedi nekoliko riječi i linija koda:
const duplicar = function (n) {
return n * 2;
};
Kada želite vratiti literalni objekt iz funkcije strelice s implicitnim povratom, morate staviti objekt u zagrade kako biste spriječili da se vitičaste zagrade interpretiraju kao početak bloka koda:
const obtenerUsuario = () => ({
nombre: "Laura",
edad: 28
});
S ovom sintaksom, streličke funkcije postaju idealni alati za pisanje kratkog i ekspresivnog koda , široko korištenog u metodama nizova, obećanjima, laganom funkcionalnom programiranju i komponentnoj logici u modernim okvirima.
Ovo u funkcijama strelica: leksički kontekst
Zaista važna razlika između streličke funkcije i klasične funkcije nije samo sintaksa: već način na koji obrađuju vrijednost `this` . Dok regularne funkcije imaju dinamičko `this` koje ovisi o njihovom imenu, streličke funkcije imaju leksički `this`.
Reći da je `ovo` leksički znači da funkcija strelice ne kreira vlastito interno `ovo` , već nasljeđuje `ovo` konteksta u kojem je definirana. Ako napišete strelicu unutar regularne funkcije, `ovo` strelice bit će isto kao i kod vanjske funkcije; ako ste u globalnom opsegu modula, ona nasljeđuje taj kontekst.
Ovo ima direktan uticaj kada koristite funkcije strelica kao metode objekta. Ako, na primjer, napišete nešto ovako:
const obj = {
nombre: "Juan",
imprimir: () => {
console.log(this.nombre);
}
};
obj.imprimir();
U ovom isječku koda, `this` unutar funkcije strelice se ne odnosi na `obj` , već na vanjski kontekst u kojem je ta strelica kreirana (na primjer, globalni objekt ili trenutni modul). Uobičajeni rezultat je da `name` nije definiran ili ne pokazuje na očekivanu vrijednost.
S druge strane, leksičko nasljeđivanje od `this` je blagoslov pri radu s povratnim pozivima unutar metoda . Umjesto pribjegavanja trikovima poput pohranjivanja `this` u međuvarijablu (`var that = this`) ili korištenja `.bind()`, strelica bez napora zadržava `this` iz roditeljskog konteksta:
function Contador() {
this.valor = 0;
setInterval(() => {
this.valor++;
console.log(this.valor);
}, 1000);
}
new Contador();
U ovom primjeru, funkcija proslijeđena funkciji setInterval je strelica , tako da je ovo unutar nje i dalje instanca klase Counter, što znatno olakšava posao i izbjegava tipične kontekstualne greške.
Pored ovoga, funkcije strelica također ne kreiraju vlastitu verziju objekta arguments , tako da ako trebate pristupiti svim dinamički proslijeđenim argumentima, morat ćete koristiti operator rest (...params) ili pribjeći normalnoj funkciji, ovisno o slučaju.
Leksički opseg i zatvaranje pomoću funkcija strelica
Još jedna posljedica dizajna strelica je da dijele isti leksički opseg kao i njihov roditeljski kontekst . To je primjetno ne samo kod `this`, već i kod varijabli kojima mogu pristupiti, što se vrlo dobro uklapa u koncept closures (zatvorenih elemenata).
Zamislite funkciju koja vraća drugu unutrašnju funkciju. Ako je unutrašnja funkcija strelica, ona može nastaviti koristiti varijable definirane u vanjskoj funkciji , čak i nakon što je vanjska funkcija završila s izvršavanjem.
const exterior = () => {
const variableExterna = "Valor exterior";
return () => {
console.log(variableExterna);
};
};
const interna = exterior();
interna(); // "Valor exterior"
U ovom slučaju, interna funkcija je strelica koja koristi isti leksički opseg za nesmetan pristup eksternoj varijabli. Ovaj obrazac se široko koristi za kreiranje konfiguriranih funkcija, fabrika i enkapsulirane logike.
Funkcije strelica u odnosu na normalne funkcije: poređenje
Ako klasičnu funkciju i strelicu postavimo jednu pored druge, vidimo nekoliko relevantnih razlika koje utiču na to kako ih koristimo u svakodnevnoj praksi:
- Sintaksa i konciznost: Normalna funkcija se zapisuje kao
function nombre() {}, dok je strelica običnoconst nombre = () => {}U jednostavnim slučajevima, strelica je očito kraća i čitljivija. - Podizanje: tradicionalne deklaracije funkcija su elevanStoga ih možete pozvati prije njihove definicije u kodu. Strelice, koje su izrazi dodijeljeni varijablama ili konstantama, Ne mogu se koristiti prije nego što budu definirani.
- Kontekst ovoga: Klasična funkcija ima dinamičko `this` koje se mijenja ovisno o tome kako se poziva; strelica ima leksički `this`, naslijeđen iz konteksta u kojem je deklarirana.
- Koristite kao graditelj: Normalne funkcije se mogu pozvati sa novi za kreiranje objekata; funkcije strelica Ne mogu se koristiti kao građevinari.
- Argumenti objekta: U tradicionalnim funkcijama imate argumenti; u strelicama ovaj objekat ne postoji i obično se koriste preostali parametri.
Zbog svih ovih razloga, najčešće se koriste streličaste funkcije za povratne pozive, brze transformacije podataka i logiku gdje vam nije potreban vlastiti `this` , a klasične funkcije se zadržavaju za konstruktore, metode objekata koje zahtijevaju dinamički `this` ili slučajeve kada vas zaista zanima objekt `arguments`.
Uobičajena upotreba funkcija strelica
U modernom JavaScriptu, svakodnevna upotreba je puna funkcija sa strelicama, posebno u povratnim pozivima, metodama nizova i funkcionalnom kodu . One su prirodna zamjena za tradicionalne anonimne funkcije.
Vrlo čest slučaj upotrebe je njihovo prosljeđivanje kao povratnih poziva u metodama niza kao što su forEach, map, filter ili reduce. Sintaksa postaje mnogo kraća, a nasljeđivanjem ovoga, bolje se uklapaju u mnoge scenarije:
const nombres = ;
nombres.forEach(nombre => {
console.log(`Hola ${nombre}`);
});
U poređenju sa pisanjem iste stvari pomoću funkcije, verzija sa strelicom je mnogo čišća i ne gubi na čitljivosti:
.map(function (n) {
return n * 2;
});
.map(n => n * 2);
Kada izvodite složenije transformacije, metode poput filter i map također očito imaju koristi od sintakse strelica:
const nums = ;
const pares = nums.filter(n => n % 2 === 0);
const duplicados = nums.map(n => n * 2);
console.log(pares); //
console.log(duplicados); //
Strelice se također često koriste u povratnim pozivima tajmera ili događaja , pod uvjetom da rukovanje `this`-om koje pružaju odgovara vašim potrebama. U `setTimeout`-u, na primjer, vrlo je uobičajeno vidjeti:
setTimeout(() => {
console.log("Mensaje con delay");
}, 1000);
Prilikom iteracije kroz nizove s funkcijama forEach i arrow , ova kombinacija je posebno pogodna za izvođenje jednostavnih operacija na svakom elementu bez gubitka jasnoće:
const nombres = ;
nombres.forEach(nombre => console.log("Hola " + nombre));
Kada je prikladno, a kada ne koristiti funkcije strelica?
S obzirom na sve navedeno, moglo bi se činiti da su streličaste funkcije rješenje za sve, ali stvarnost je da one nisu uvijek najbolji izbor . Postoje specifične situacije u kojima je poželjnije držati se tradicionalnih funkcija.
Streličke funkcije su odlične kada su vam potrebni kratki povratni pozivi, transformacije podataka, kompozicija funkcija i nasljeđivanje `this` iz okruženja . U tim slučajevima izbjegavate standardni kod, pojednostavljujete svoj kod i olakšavate praćenje logike vašeg programa.
Međutim, ne biste ih trebali koristiti kao konstruktore , jer ne rade direktno sa `new`. Ako vam je namjera kreirati prilagođene tipove, klase ili prototipove, nastavite koristiti regularne funkcije ili sintaksu `class`.
Također nije dobra ideja deklarirati metode objekta poput `strelica` ako očekujete da `ovo` pokazuje na taj objekt . Već smo vidjeli da `strelica` ne definira vlastito `ovo`, tako da vrijednost neće biti ono što mnogi programeri očekuju prilikom čitanja koda, što dovodi do suptilnih grešaka.
Čak i u stvarima poput nekih rukovatelja događajima ili API-ja koji svoje ponašanje zasnivaju na vrijednosti `this` , normalna funkcija može biti prikladnija, upravo zato što je tamo važno da se `this` mijenja ovisno o tome ko poziva funkciju.
Konačno, ako vam je potreban fleksibilan pristup svim argumentima primljenim putem arguments , možda će vam biti prirodnije koristiti tradicionalnu funkciju ili redizajnirati interfejs funkcije tako da koristi rest parametre.
Funkcije i metode u dizajnu JavaScript aplikacija
Kada shvatite razlike između funkcija sa strelicama, klasičnih funkcija i metoda , zaista ih možete iskoristiti za strukturiranje kompletnih JavaScript aplikacija, kako frontend tako i backend.
"Normalne" funkcije su odličan resurs za zadatke opšte namjene, kreiranje objekata (konstruktora) i logiku gdje želite preciznu kontrolu nad `this` . Njihove mogućnosti podizanja također mogu pojednostaviti organizaciju koda u velikim datotekama ako ih pažljivo rasporedite.
Metode, s druge strane, funkcioniraju kao specifične akcije povezane s podacima objekta . Savršeno je logično da se `this` odnosi na sam objekt, jer metode enkapsuliraju i ponašanje i stanje. Prilikom definiranja metode u prototipu ili klasi, prirodno je koristiti `function` ili skraćenu sintaksu za metode objekta.
Funkcije strelica dolaze u obzir kao komplementarni element: služe za povezivanje elemenata, transformaciju podataka i definiranje povratnih poziva bez vizualnog nereda . Njihova široka upotreba u modernim bibliotekama, okvirima i API-jima nije slučajnost: one se vrlo dobro uklapaju u deklarativni stil većeg dijela današnjeg koda.
Razumijevanje uloge svakog tipa funkcije, poznavanje kada koristiti jednu ili drugu funkciju i savladavanje ponašanja funkcije `this` omogućava vam da pišete robusniji, čistiji i skalabilniji JavaScript kod , izbjegavajući mnoge klasične zamke jezika.
Sve što smo vidjeli o klasičnim funkcijama, parametrima, metodama, funkcijama strelicama, leksičkom opsegu i posebnom rukovanju `this` već vam daje solidnu osnovu za rješavanje stvarnih projekata: od malih skripti do složenih aplikacija, odabir prave vrste funkcije za korištenje u svakom dijelu čini razliku između haotičnog koda i koda koji svatko može rado čitati, održavati i poboljšavati.