JavaScript Arrow funkcije

Arrow funkcije su skraćeni način pisanja funkcija, prema ECMAScript 6 standardu. Još ih nazivaju i "fat arrow" funkcijama, zbog načina navođenja pomoću znaka => koji u stvari podseća na logičko "sledi".

Način pisanja Arrow funkcija je prilično jednostavan:

(parametri) => {telo funkcije};

Parametri se zadaju u zagradi, uz moguć izuzetak kada postoji samo jedan parametar.

Više parametara: (p1, p2, p3...) => ... Jedan parametar: param => ... Bez parametara: () => ...

Postoje i različite varijante kada navodimo telo funkcije. Ako imamo više iskaza (tj. naredni), smeštamo ih (kako smo i navikli) unutar vitičastih zagrada.

... => {iskaz; iskaz; ...};

Ako je funkcija toliko jednostavna da ceo rezultat funkcije možemo da smestimo u jedan izraz, možemo samo navesti taj izraz i to bez naredbe return. Međutim, ako je, sticajem okolnosti, povratna vrednost objekat, znamo da se on smešta unutar vitičastih zagrada. Pošto se na isti način navodi i telo funkcije, u tom slučaju se ceo objekat mora smestiti i u obične zagrade.

... => izraz; ... => ({svojstvo: vrednost, svojstvo: vrednost...});

Primer - Skraćeni zapis funkcije

Klasična funkcija

U ovom primeru vidimo prost poziv callback funkcije za poziv metoda reduce() nekog niza. Poželeli smo da na ovaj način sve elemente niza spojimo u jedan string.


  var imena = ["Borko", "Miloš", "Ana", "Dušan", "Anđelka"];
  
  var svi = imena.reduce( function(rez,a) {
    return rez + " " + a;
  });
  
  console.log(svi);  // " Borko Miloš Ana Dušan Anđelka"
};

Kao callback metoda reduce() koristimo običnu funkciju kojoj se prosleđuju dva parametra. Prvi je dosadašnji rezultat, a drugi trenutni elemenat niza. Da bismo dobili jedan string od svih članova niza, prosto dodajemo trenutni elemenat niza na dosadašnji rezultat (sa razmakom). Tako "nakupljen" rezultat će na kraju biti i rezultat metoda reduce() i smešten u promenljivu svi.

Arrow funkcija

Sada možemo videti kako izgleda taj isti deo programa, ali kada kao callback koristimo Arrow funkciju.


  var imena = ["Borko", "Miloš", "Ana", "Dušan", "Anđelka"];
  
  var svi = imena.reduce( (rez,a) => rez + " " + a );
  
  console.log(svi);  // " Borko Miloš Ana Dušan Anđelka"
};

Pogledajte koliko je sve kraće zapisano pomoću Arrow funkcije. Najpre su parametri navedeni u zagradi, onda se navodi => strelica i za njom samo izraz koji se vraća kao rezultat (ono što je bilo u return iskazu klasične funkcije).

js-fun-arrow-1

Specijalne osobine Arrow funkcija

Arrow funkcije su uvek anonimne. To znači da ne mogu imati svoje sopstveno ime, već mogu biti pozvane samo putem reference.

Najvažnija razlika u odnosu na obične funkcije je što Arrow funkcije ne definišu objekat this. Ako ste zaboravili o čemu se radi, pročitajte naš raniji tekst o objektu this i kako on funkcioniše. To znači da će this biti preuzet iz nadređene funkcije - unutar koje je Arrow funkcija deklarisana. Ako nadređena funkcija ne postoji, objekat this će se odnositi na objekat Window (što je uobičajeno ponašanje za funkcije).

Mehanizam rada ovih funkcija je da se this definiše u trenutku inicijalizacije Arrow funkcije, i ostaje fiksiran za koji god kontekst je u tom trenutku "aktuelan". Dakle, this neće biti definisan tek kada se funkcija pozove, nego čim se funkcija "proglasi" tokom izvršavanja nadređene funkcije. U literaturi se ovo naziva "leksički this".

Primer - Leksički this

Reciomo da imamo objekat imena u kome se nalaze svojstva niz (niz nekih imena) i najduze (string u kome će se naći najduže ime iz niza). Tu je i metod calcNajduze() koji prolazi kroz sva imena iz niza i pronalazi najduže ime koje smešta u svojstvo najduze.

Najpre ćemo napraviti ovaj objekat koristeći obične funkcije.


var imena = {
  niz: ["Ana Štilić", "Bojana Najdić", "Aleksandar Radosavljević", "Borko Đorić"],
  najduze: "",

  calcNajduze: function() {
    var _self = this;
    this.niz.forEach( function(x) { 
      if (x.length > _self.najduze.length)
        _self.najduze = x;
    });
  }
};

Veoma nas interesuje sadržaj metoda calcNajduze(). Unutar njega pozivamo metod forEach() za niz. Znamo da forEach() funkcioniše tako što za svaki član niza poziva zadatu callback funkciju u kojoj ispitujemo da li je dužina tog elementa niza veća od najveće dužine.

Međutim, bilo bi pogrešno ako bismo unutar callback funkcije pokušali da koristimo objekat this. U ovom slučaju this bi predstavljao objekat window koji nema svojstvo najduze. Ima više načina da se ovo reši (npr. vezivanjem ispravnog objekta this za funkciju), a ovde smo se odlučili za korišćenje closure mehanizma. U nadfunkciji smo najpre "zapamtili" this u promenljivoj _self, kojoj onda pristupa callback podfunkcija.

Ovo može da bude malo nezgrapno, pošto kao programeri moramo stalno da razmišljamo i o kontekstu izvršavanja svake podfunkcije a posebno može da nas boli glava kod asinhronog izvršavanja kada se callback funkcije izvršavaju ko-zna-kad. Hajde da sada iskoristimo blagodeti ES6 specifikacije.


var imena = {
  niz: ["Ana Štilić", "Bojana Najdić", "Aleksandar Radosavljević", "Borko Đorić"],
  najduze: "",

  calcNajduze() {
    this.niz.forEach( (x) => { 
      if (x.length > this.najduze.length)
        this.najduze = x;
    });
  }
}

U ovom slučaju, kao callback koristimo Arrow funkciju. Pošto ona ne definiše svoj this, objekat this se preuzima iz nadređene funkcije. Ovde je to metod calcNajduze() i samim tim this je upravo objekat imena, kao što nam i treba.

js-fun-arrow-2

Sve ovo znači da Arrow funkcije nisu dobri kandidati da budu metodi objekata, pošto bismo upravo tu i koristili objekat this.

Arrow funkcije, takođe ne mogu biti konstruktori. Znači, da Arrow funkcije ne mogu da budu korišćene uz operator new. Ako vam nije poznato o čemu pričamo, pročitajte tekst o konstruktorskim funkcijama.

Osim toga, Arrow funkcije nemaju arguments listu, tj. nemoguće je pristupiti parametrima funkcije na taj način, što može biti nezgodno ako funkciji prosleđujemo varijabilan broj parametara. Kako koristimo ovu listu, možete čitati u tekstu o parametrima funkcije. Srećom, ovaj tip funkcija dobro funkcioniše sa rest operatorom, što nam rešava bar taj problem.

Sve ovo nam govori da su Arrow funkcije prilično "uprošćene", u smislu da ih koristimo kad nam je potrebna mala, jednostavna funkcija za neki prost zadatak i želimo brzo i lako da je deklarišemo. Suštinski, Arrow funkcije su zamišljene najpre da budu callback funkcije, iako se mogu koristiti i kao obični potprogrami (koji nam inače služe radi lakše organizacije programa).

U stvari, ES6 entuzijasti sada govore i o potpunom izbacivanju klasičnih funkcija:

Nismo sigurni u tako skoru "smrt" klasične function(){} deklaracije, posebno kad imamo u vidu da je web programerima i dalje najlakše korišćenje takvih funkcija kada ih koriste kao event handlere, tj. za razrešavanje događaja nad elementima web stranice.

Ne kažemo da je nemoguće koristiti Arrow funkcije za događaje nad web elementima, ali mnogi programeri se neće odricati navike korišćenja objekta this, i menjati ga za nezgrapnije event.target, odnosno event.currentTarget.

Još jedna situacija kada ćemo teško izbeći "klasične" funkcije je proširivanje prototipa nekog objekta. Ako želimo da uvedemo novi metod koji će važiti za sve objekte kreirane istim konstruktorom i pogotovo ako u toj metodi želimo da pristupimo samom objektu, biće nam potreban objekat this, koji Arrow funkcije ne definišu.

Primer - Kada ne treba koristiti Arrow funkcije

Proširivanje prototipa objekta

Pogledajmo sledeći primer iz programerske prakse: JavaScript stringovi imaju mogućnost iščitavanja znaka na zadatoj poziciji, korišćenjem metode charAt(). Recimo da bismo voleli da postoji i metod setCharAt() kojim bismo mogli da zamenimo znak na nekoj poziciji.

Ništa problematično - JavaScript nam omogućava da bilo kom objektu proširimo prototip i tako dodamo novu funkcionalnost. Evo kako bismo to uradili na klasičan način:


  String.prototype.setCharAt = function(ind, znak) {
    // vraća novu "verziju" stringa sa promenjenim znakom
    return (this.substr(0,ind) + znak + this.substr(ind+1));
  };

  var s = "webxstudy".setCharAt(3, "n");  // s == "webnstudy"

Ovo je jako uprošćen metod - samo da vidimo koncept rada. Nismo vršili proveru indeksa (da nije negativan i da nije veći od dužine stringa), niti smo obezbedili da zadati znak mora biti samo jedan karakter. Kao što vidimo, moramo da se oslanjamo na objekat this, pošto se tu i nalazi sadržaj stringa!

Ako bismo pokušali to isto da uradimo sa Arrow funkcijom, napravili bismo grešku.


  String.prototype.setCharAt = (ind, znak) => this.substr(0,ind) + znak + this.substr(ind+1);

  var s = "webxstudy".setCharAt(3, "n");  // GREŠKA - objekat this nema metod substr()

Pošto Arrow funkcija nema sopstveni this, tačnije this se ne definiše tokom izvršavanja zavisno od konteksta izvršavanja funkcije, this koji pozivamo najverovatnije predstavlja objekat window.

Upravo zbog toga, Arrow funkcije nisu pogodne da njima deklarišemo metode objekata.

  1. Mozilla Developer Network, Arrow functions
  2. Hackernoon, Javascript ES6 — Arrow Functions and Lexical `this`
  3. dmitripavlutin.com, When 'not' to use arrow functions
  4. StackOverflow, When should I use Arrow functions in ECMAScript 6?
  5. Exploring ES6, Arrow functions
  6. D. Sheiko (2015): JavaScript Unlocked, Packt, Birmingham
Svi elementi sajta Web'n'Study, osim onih za koje je navedeno da su u javnom vlasništvu, vlasništvo su autora i ne smeju se koristiti, u celosti ili delimično bez pismenog odobrenja autora. To uključuje tekstove, slike, ilustracije, animacije, prateći grafički materijal i programski kod.
Ovaj sajt koristi tehnologiju kolačića (cookies). Detaljnije o tome možete pročitati u tekstu o našoj politici privatnosti.