Uvod

js this

Programeri koriste jedan od ova dva mehanizma:

  • leksički opseg vidljivosti
  • “this mehanizam”

Mehanizam koji koristi rezervisanu reč “this” pruža elegantan način za implicitno “prosledjivanje” reference na odredjeni objekat, što dovodi do čistijeg dizajna i olakšava višekratnu upotrebu koda. Programeri koji izbegavaju da koriste this mehanizam, uglavnom to rade zbog ne razumevanja principa rada tog mehanizma.

“This” nije promenjiva već specijalana reč programskog jezika (operator) i predstavlja vezu koja ukazuje na neku referencu (objekat). Aktivira se tj. dobija značenje tek u trenutku kada se pozove (invoke) funkcija koja ga sadrži.

Ne zavisi od mesta gde je funkcija deklarisana, već isključivo od mesta i načina na koji je funkcija pozvana!

Pravila za odredjivanje značenja “this”

Ceo postupak odredjivanja značenja se zasniva na prepoznavanju “ko” i na koji način poziva funkciju koja sadrži “this”. Redosled radnji je sledeći:

  1. Prvo se proverava da li je konstruktorska funkcija pozvana sa operatorom new(). Ukoliko jeste pozvana na ovaj način onda je vrednost “this” objašnjena pod tačkom a) Pozivanje funkcije sa operatorom “new” ali ako nije pozvana na ovaj način onda se vrši…
  2. … kontrola da li je funkcija pozvana uz pomoć metoda call(), apply() (ili bind). Ukoliko jeste pozvana na ovaj način onda je vrednost “this” objašnjena pod tačkom b) Pozivanje sa call(), apply() ili bind(), ali ako nije pozvana na ovaj način onda se vrši …
  3. …kontrola da li je funkcija pozvana kao metoda objekta. Ukoliko jeste pozvana na ovje način onda je vrednost “this” objašnjena pod tačkom c) Pozivanje metode objekta, ali ako nije pozvana na ovaj način onda je…
  4. podrazumevano povezivanje, koje je objašnjeno pod tačkom d) Podrazumevano povezivanje

Ukoliko je nekim slučajem korišćeno više različitih pravila, primenjuje se prioritet prema redosledu iz prethodno opisanog postupka.

a) Pozivanje funkcije sa operatorom “new”

Nakon pozivanja konstruktorske funkcije sa operatorom new, this unutar konstruktorske funkcije ukazuje na instancu objekta.

b) Pozivanje sa call(), apply() ili bind()

this bind apply call

JavaScript-u funkcije su objekti, u samom jeziku postoje predefinisane metode za njih i one mogu da se koriste na bilo kojoj funkciji. Ugradjene metode koje mogu da promene objekat na koji ukazuje operator this su: apply(), call() i bind(). Osnovna razlika izmedju bind() metode i apply()/call() je ta što apply()/call() metode odmah pozivaju (eng. invoke) funkciju, dok metoda bind() to ne radi, pa je potrebno pored nje dodatno pozvati funkciju.
Metoda bind() za argument prihvata objekat za koji treba “privezati” this iz funkcije. Na sledećem primeru je prikazano “vezivanje” this u funkciji definisanoj van objekta koristeći bind(), za željeni objekat.

Kod metoda call() i apply() prvi argument definiše na koji objekat će ukazivati this, dok ostali argumenti prosledjuju parametre potrebne osnovnoj funkciji. Jedina razlika izmadju metoda apply() i call() je način u kom metoda prihvata argumente koji se prosledjuju osnovnoj funkciji. Metoda call() dodaje ostale argumente sa zarezom, dok metoda apply() dodaje ostale elemente kao niz elemenata.

SAVET:
Za lakše pamćenje u kakvom obliku koja metoda prihvata argumente koristite prva slova metoda:
Apply <=> Array ili Call <=> Comma.

c) Pozivanje metode objekta

Kada se funkcija nalazi u okviru nekog objekta, onda pri pozivanju metode objekta ključna reč this ukazuje na taj objekat (koji je “vlasnik” metode).

Pošto this ukazuje na objekat osoba možemo preko nje iz metode da pozovemo svojstva objekta:

Primer

Na prethodnom primeru funkcija foo() nije u direktnom vlasništvu objekta “spoljniObjekat”, već svojstvo objekta ima spoljnu referencu na tu funkciju. Za odredjivanje objekata na koji ukazuje operator this je bitno mesto pozivanja funkcije koja angažuje this, tako da u ovom slučaju pošto se funkcija poziva iz objekta “spoljniObjekat”, ona se ponaša kao metoda objekta pa this ukazuje na objekat!

Primer

U sledećem primeru se vidi da je važan samo “najbliži” (“poslednji”) objekat u izrazu za pozivanje funkcije tj. da operator this ukazuje na njega.

d) Podrazumevano ponašanje

Podrazumevano ponašanje se koristi kada ne važi ni jedno drugo pravilo. Kada se primeni ovo pravilo this ukazuje na globalni objekat (window).

Primer

U sledećem primeru funkcija foo() se poziva iz globalnog konteksta a ne kao metoda objekta, stoga this ukazuje na globalni objekat:

NAPOMENA:
Najčešće programer ne želi da specijalna reč this ukazuje na globalni objekat, ali ukoliko se ipak primeni pravilo podrazumevanog povezivanje (najčešće zbog nesvesne greške pri programiranju) kompajler neće izbaciti nikakvu grešku! Stoga je preporuka da se uključi strict mod jer u tom slučaju kompjler za svaku referencu na globalni objekat izbacuje grešku “undefined”, pa programer može na vreme da uoči grešku.
Primer

Tako isti primer kao prethodni ali sa uključenim strict modom vraća undefined

Problemi sa “this” i njihova rešenja

Kroz sledeće primere ću pokušati da objasnim slučajeve kada je ponašanje reči this zbunjujuće tj. kada se this ne ponaša onako kako na prvi pogled očekujemo. Sa stanovišta “this” je jedino važno odakle se poziva funkcija koja sadrži operator this.

Problematični slučajevi

a) “This” unutar closure

Problem koji se javalja kod clousure funkcija je taj što one iako su unutar objekta nemaju pristup objektu preko svog operatora this (jer ukazuje na globalni objekat). Clousure funkciju poziva druga funkcija a ne objekat stoga se koristi “podrazumevano pravilo”.

U ovom primeru se poziva metoda osoba.punoIme(), koja poziva funkciju spajanjeImena(). Pošto funkcija spajanjeImena() nije pozvana iz objekta (nego je pozvala druga funkcija) koristi se “podrazumevano pravilo”. Stoga pri pozivanju metode punoIme() se dobija undefined, jer this ukazuje na globalni objekat window, a globalni objekat window nema svojstva window.ime i window.prezime.

Rešenje

Najbolje rešenje ovog problema je uz pomoć bind() metode, koja veže “this” funkcije spajanjeImena() sa “this” funkcije punoIme(). Nakon toga pošto this metode punoIme() ukazuje objekat “osoba”, pa će i this naše unutrašnje funkcije ukazuje na objekat “osoba”.

Pored ovoga načina, problem možemo rešiti koristeći mehanizam “self=this” ili arrow funkciju jer “parent” funkcija ima pristup objektu (preko svoga “this”).

b) “This” unutar setTimeout()

Funkcija setTimeout() poziva callback funkciju nakon nekog vremenskog intervala (što znači da je funkcija pozvana iz druge funkcije, a ne objekta). Stoga se primenjuje “podrazumevano ponašanje”, pa this ukazuje na globalni objekat. Na sledećem primeru this ukazuje na globalni objekat:

Ovaj konstruktor može da se napiše i drugačije tako što ćemo da izvučemo callback funkciju kao novo svojstvo prototype objekta, pa onda možemo da ga pozivamo sa this.callback:

Ovaj problem se rešava uz korišćenje metode bind(). Potrebno je “this” unutar callback funkcije vezati za svaki novonapravljeni objekat. Stoga parametar metode bind() treba da bude objekat na koji želimo da “this” ukazuje, a to je u ovom slučaju opet “this” (nakon korišćenja operatora “new” “this” ukazuje na novonapravljeni objekat)

c) “This” u tzv. “izvučenoj metodi”

Metoda je samo svojstvo objekta koje ima referencu na neku funkciju. Kada metodu objekta dodelimo nekoj promenjivoj mi smo promenjivoj dodelili referencu na funkciju. Stoga treba razumeti da pozivanjem te promenjive mi ne pozivamo metodu objekta nego običnu funkciju.

Primer

Prethodni primer sa gledišta this izgleda ovako:

Iz priloženog se vidi da se poziva funkcija prikazPunogImena(), a ne metoda objekta. Stoga se koristi “podrazumevano pravilo” kada this ukazuje na globalni objekat window koji nema svojstvo window.ime i window.prezime pa vraća undefined, undefined.

Rešenje problema

Ovaj problem se rešava sa bind() metodom, kojom ćemo explicitno povezati this iz funkcije sa objektom “osoba”, nakon čega više nije bitno odakle se funkcija poziva.

d) “This” unutar Event handler-a

This unutar “inline” event handler

Jedna od najvećih mana ovog zastarelog načina registrovanja dogadjaja je da this ukazuje na globalni objekat.

“This” unutar tradicionalnog event handler-a i W3C model

Kod tradiocionalnog registrovanja dogadjaja this ukazuje na elemenat koji je bio okidač za dogadjaj.

“This” unutar W3C modela event handler-a

Kod W3C modela sa addEventListener() this ukazuje na elemenat koji je bio okidač za dogadjaj.

Postupci za rešavanje problema

Ukoliko “this” ne ukazuje na ono na šta mi želimo, postoje načini sa kojima možemo da promenimo značenje ključne reči “this”.

a) bind(), call(), apply()

Jedan od načinina da se predefiniše značenje “this” je korišćenje jedne od metoda: bind(), call(), apply(). Sve tri metode kroz argument definišu objekat za koji treba “privezati” this iz funkcije. Stoga je dovoljno pri pozivanju metode nadovezati i neku od pomenutih metoda:

b) Arrow function

Sa novom verzijom JavaScript-a ECMAScript 6 dolazi novi način pisanja funkcije arrow function. Arrow function umesto da primenjuje neko od 4 standardna pravila za povezivanje this, ona sasvim zaobilazi standardni mehanizam i povezuje this sa okružujućim opsegom vidljivosti (leksički opseg vidljivosti). Kod pozivanja ovih funkcija this nasledjuje značenje this iz okružujuće funkcije. Uprošteno this arrow funkcije se izjednačava sa onim na šta bi ukazivalo this iz spoljne funkcije koja okružuje arrow funkciju (ona unutar sebe ima ugradjen engine koji praktično zamenjuje poznati mehanizam: “self=this”). Kada se koristi arrow funkcija nije bitan način i mesto pozivanja funkcije, već samo na šta ukazuje this iz spoljne funkcije.

Značenje koje je operator this dobio unutar arrow funkcije se ne može kasnije promeniti! Tako da ne može biti pregaženo ni sa predefinisanim metodama bind(), apply() ili call() pa čak ni sa operatorom “new”.

Primer (closure)

Već pomenuti problem iz prehodnog dela se takodje može rešiti korišćenjem arrow funkcije:

Rešenje

Pošto se koristi arrow funkcija ona nasledjuje značenje this iz okružujuće funkcije koja “obmotava” arrow funkciju. U ovome slučaju okružujuća funkcija je punoIme(), tako da će this unutar arrow funkcije ukazivati na isto što i funkcija punoIme() tj. this će ukazivati na objekat “osoba”.

c) Mehanizam “self=this”

Ovim mehanizmom se ustvari i ne koristi operator this nego se zaobilazi njegova primena. Problem koji se javalja kod clousure funkcija je što one iako su unutar objekta nemaju pristup objektu preko svog operatora this (jer ukazuje na globalni objekat). Medjutim, njena “parent” funkcija ima pristup objektu, preko svoga operatora “this” i to ćemo iskoristi za rešavanje problema.
Ukoliko sačuvamo referencu na objekat (na koji ukazuje “this” spoljne funkcije) u okviru nove promenjive (npr.“self”), omogući ćemo unutrašnjoj funkciji (clousure) pristup objektu preko te nove promenjive “self”.

Podelite:

2 Responses to “Značenje operatora “this” u JavaScript-u”

  1. dalibor

    Klikni me

    var el = document.getElementById(“btn”);
    el.addEventListener(“click”, mojaFunkcija);

    function mojaFunkcija (){
    console.log(this.id); //Vraca: “dugme”
    }

    Malo me zbunjuje ovaj opis, zar ne bi trebalo da bude kod get elementById(“dugme”)
    Kod poslednjeg eventhandlera

Ostavite komentar