Prototipno nasljedjivanje
Jednostavno receno, prototipsko nasljedjivanje se odnosi na mogucnost pristupa svojstvima jednog objekta iz drugog objekta, sto umanjuje potrebu dupliciranja koda. Klase su implementirane na osnovu prototipa i ovdje pricamo vise o tome sta se desava u pozadini.
Svaki objekat ima privatno svojstvo [[Prototype]]
koje za vrijednost ima ili referencu na drugi objekat ili null
. U primjeru ispod munchkin
objekat za prototip dobija cat
objekat.
let cat = {
says: 'meow',
talk() {
console.log(`${this.name} says ${this.says}!`);
// this je uvijek objekat prije tacke _____.svojstvo
}
};
let munchkin = {
name: 'Odie',
breed: 'Munchkin',
__proto__: cat
};
munchkin.talk(); // Odie says meow!
munchkin.talk()
metoda ne postoji u munchkin
objektu, ali kako je njegov prototip cat
objekat, to je sledece mjesto gdje ce JavaScript engine nastaviti potragu. Potom bi je trazio u prototipu cat
objekta, taj objekat potom ima svoj [[Prototype]]
, sve dok trazena metoda ne bude pronadjena ili dok pretraga ne dosegne kraj lanca, odnosno vrijednost null
koja nema prototip i tu pretraga zavrsava sa greskom.
[[Prototype]]
nije samo apstraktni koncept i da bi ga vidjeli mozemo napisati console.log(munchkin)
i pregledati rezultat u DevTools konzoli.
× Object breed: "Munchkin" name: "Odie" × [[Prototype]]: Object says: "meow" > talk: ƒ talk() × [[Prototype]]: Object > constructor: ƒ Object() > hasOwnProperty: ƒ hasOwnProperty() ...
Istom prototipu je moguce pristupiti direktno:
console.log(munchkin.__proto__); // cat
console.log(munchkin.__proto__.__proto__); // Object.prototype
// Vise o njemu kasnije
console.log(munchkin.__proto__.__proto__.__proto__); // null
// Dosegli smo kraj lanca nasljedjivanja za ovaj objekat
[[Prototype]]
referenca ne moze ici u krug.
let predator = {};
let cat = { __proto__: predator };
let munchkin = { __proto__: cat };
predator.__proto__ = munchkin; // TypeError: Cyclic __proto__ value
for..in
petlje su svjesne naslijedjenih svojstava sto znaci da ce se data akcija ponoviti i za nasljedjena svojstva (nije slucaj sa klasama). Vidljive su petljama jer deskriptor enumerable
ovih svojstava nije podesen da bude false
. Deskriptore je moguce podesiti rucno.
let predator = { dangerous: 'very' };
let cat = { says: 'meow', __proto__: predator };
let munchkin = { name: 'Odie', __proto__: cat };
for (let prop in munchkin) {
console.log(prop); // name, says, dangerous
}
Postoji nacin da testiramo da li je svojstvo dio objekta ili ne sa obj.hasOwnProperty(key)
— vraca true
ili false
.
for (let key in munchkin) {
if (munchkin.hasOwnProperty(key)) {
console.log(key); // name
} else {
console.log(key); // says, dangerous
}
}
Ili mozemo objekat pretvoriti u niz. On nece uzeti u obzir nasljedjena svojstva. Za nizove je prikladnije koristiti for..of
petlju:
let munchkinArray = Object.keys(munchkin); // ['name']
for (let key of munchkinArray) {
console.log(key); // name
}
Definisanje prototipa
U primjerima iznad je koristeno svojstvo __proto__
. On je (getter i setter) svojstvo za pristupanje prototipu objekta. Smatra se zastarjelim, ali ga je prihvatljivo koristiti za vrijeme kreiranje objekta {__proto__: ...}
. Preporuceni nacin za dodjeljivanje prototipa za vrijeme kreiranje objekta je Object.create(prototype, propertiesObject)
(ili koristenjem klase). Prvi parametar je objekat koji ce novi objekat naslijediti, a kroz drugi opcioni parametar propertiesObject
mozemo dodavati svojstva i podesiti njihove deskriptore.
let munchkin = Object.create(cat, {
'name': {
value: 'Odie',
enumerable: true, // prikazuje se u petljama
writable: true, // promjenjiva vrijednost
configurable: true // deskriptori se mogu modifikovati, a svojstvo brisati
},
'breed': {
value: 'Munchkin',
enumerable: true
// nenavedeni deskriptori imaju vrijednost false
}
});
Sada necemo definisati svojstva kroz drugi parametar u Object.create()
. Ukoliko u sklopu njega ne navedemo neki deksriptor, njegova vrijednost ce biti false
. Ako svi trebaju imati vrijednost true
navodjenje svakog deskriptora je suvisno. Smislenije je dodati svojstva nakon kreiranja objekta:
let munchkin = Object.create(cat);
munchkin.name = 'Odie';
// ili __proto__ *za vrijeme kreiranje objekta*
let korat = { __proto__: cat, name: Whiskers, breed: 'korat' };
Za mijenjanje prototipa postojecem objektu koristimo Object.setPrototypeOf(obj, proto)
metodu:
let predator = { dangerous: 'very' };
let cat = { says: 'meow' };
let munchkin = { name: 'Odie' };
cat.__proto__ = predator; // zastarjelo
Object.setPrototypeOf(munchkin, cat);
console.log(munchkin.dangerous); // very
Medjutim, dinamicno dodjeljivanje prototipa treba izbjegavati. Vecina interpretera JavaScript koda je optimizovalo mehanizam prototipnog nasljedjivanja. Ddefinisanje prototipa objekta nakon njegovog kreiranja ugrozava ovu optimizaciju. Ukoliko performans i brzina izvrsavanja koda nije vazna, onda ovo nije problem (ali onda prototip gubi svrhu). U suprotnom, dinamicno definisanje prototipa treba izbjegavati ako je moguce dodijeliti prototip za vrijeme kreiranja objekta.
Za dobijanje prototipa nekog objekta umjesto __proto__
svojstva tu treba koristiti Object.getPrototypeOf(obj)
metoda.
let cat = { says: 'meow' };
let munchkin = Object.create(cat);
// munchkin.__proto__; // zastarjelo
Object.getPrototypeOf(munchkin); // cat
Jos jedan nacin za definisanje prototipa objekta je kroz konstruktor..
Funkcija.prototype svojstvo
Za razumijevanje ove teme je, izmedju ostalog, neophodno poznavanje konstruktora i operatora new
. Ukratko, konstruktor je funkcija koja inicijalizuje objekat. Poziva se sa kljucnom rijeci new
i po konvenciji se imenuje sa prvim velikim slovom. Funkcija za rezultat vraca objekat i omogucava kreiranje vise slicnih objekata.
function Cat(name, breed) {
this.name = name;
this.breed = breed;
}
let odie = new Cat('Odie', 'Munchkin');
let whiskers = new Cat('Whiskers', 'Korat');
console.log(odie); // Cat {name: 'Odie', breed: 'Munchkin'}
console.log(whiskers.breed); // Korat
prototype svojstvo
Funkcije su objekti. A objekti imaju svojstva (key-value parove). Uz to funkcije su specificne po tome sto imaju predefinisano fn.prototype
svojstvo. Ovdje treba napraviti razliku izmedju .prototype
i [[Prototype]]
objekta. [[Prototype]]
je prototip koji funkcija (ili bilo koji drugi objekat nasledjuje), fn.prototype
je predefinisano svojstvo svih funkcija i sada pokusavamo shvatiti njegovu funkciju.
function funkcijaJeObj() {}
console.dir(funkcijaJeObj);
Umjesto console.log()
je koristen console.dir()
jer log prikazuje toString
reprezentaciju funkcije (njeno tijelo u string obliku koje se moze pozvati dodavanjem zagrada), dok dir prikazuje interaktivnu listu svojstava JavaScript objekta.
Rezultat u konzoli:
× ƒ funkcijaJeObj() arguments: null caller: null length: 0 name: "funkcijaJeObj" × prototype: > constructor: ƒ funkcijaJeObj() > [[Prototype]]: Object [[FunctionLocation]]: app.js:1 > [[Prototype]]: ƒ () > [[Scopes]]: Scopes[1]
Iako nismo sami dodali .prototype
svojstvo funkciji, ono je tu i vec sadrzi objekat sa nekim svojstvima: {constructor: ƒ, [[Prototype]]: Object}
.
Mozemo dodavati i modifikovati svojstva .prototype
-a, ali ono ima efekat samo kada se pozove funkcija sa kljucnom rijeci new
. Zasto? Jer se konstruktor funkcija poziva sa kljucnom rijeci new
da bi kreirala novi objekat i njeno prototype
svojstvo u procesu kreiranja postaje referenca [[Prototype]]
objekta koji je kreirala.constructor()
metoda uvijek pokazuje na samu funkciju sto ce se vidjeti u sledecem primjeru..
Kreiracemo praznu konstruktor funkciju i dodati metodu u njen .prototype
objekat. Ako pozovemo funkciju, ona ce nam vratiti prazan objekat, ali ce objekat imati pristup metodi koja je dodana .prototype
-u funkcije iz koje je nastao:
// Kreiranje prazne konstruktor fn
function Funkcija() {}
// Dodavanje metode u njeno .protoype svojstvo
Funkcija.prototype.svojstvo = function() {
console.log("Zdravo iz prototipa!");
};
let obj1 = new Funkcija();
// Jer obj1 ima referencu na .prototype za svoj [[Prototype]]
// Je isto kao da smo napisali:
// obj1.__proto__.svojstvo = function() {...}
obj1.svojstvo(); // Zdravo iz prototipa!
console.log(Funkcija.prototype.constructor); // Funkcija() {}
console.log(obj1.constructor === Funkcija); // true
U konzoli:
× Funkcija {} × [[Prototype]]: Object > svojstvo: ƒ () > constructor: ƒ Funkcija() > [[Prototype]]: Object
Ovaj odnos prototype
svojstva konstruktora i [[Prototype]]
svojstva objekta je mozda vizuelno lakse razumjeti:
Predefinisani prototipi
- Rekli smo da sve funkcije imaju
prototype
svojstvo. Medjutim, ono sluzi samo funkcijama koje se izvrsavaju sa kljucnom rijecinew
za definisanje prototipa[[Prototype]]
objekta koji cenew
inicijalizovati. - Sa druge strane, JavaScript dopusta kreiranje raznih objekata koristeci
new
, kaolet arr = new Array()
,let date = new Date()
,let obj = new Object()
,let fn = new Function()
,let map = new Map()
,let str = new String()
itd. Mozemo zakljuciti da svaki put kada kreiramo objekat za to koristimo konstruktor. Cak i njihova imena po konvenciji pocinju velikim slovom. - Ovi predefinisani konstruktori u
prototype
svojstvu imaju predefinisane metode i svojstva koje se mogu koristiti na novo kreiranom objektu jer ih je objekat naslijedio. U konzoli cemo ispisati sadrzajprototype
svojstva jednog od konstruktora da bi vidjeli kako izgleda.
console.log(Array.prototype);
// predefinisane metode za manipulisanje nizova
at: ƒ at()
concat: ƒ concat()
// ...
sort: ƒ sort()
splice: ƒ splice()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
unshift: ƒ unshift()
values: ƒ values()
Symbol(Symbol.iterator): ƒ values()
[[Prototype]]: Object
Ovo su sve one vec vrlo poznate metode za nizove. Kreiranjem objekta sa new Array()
(ili skraceno []
), novoinicijalizovani niz dobija Array.prototype
za svoj [[Prototype]]
.
let arr = new Array(); // isto kao let arr = []
console.log(arr.__proto__ === Array.prototype); // true
Ta svojstva su sada dostupna arr
objektu jer se nalaze u njegovom prototipskom lancu. Primjer metode koju nismo kreirali, ali je JavaScript engine pronasao u [[Prototype]]
i primjenio na novom objektu:
let arr = new Array(1, 2, 3, 4);
console.log(arr.at(-1)); // 4
Ispod cemo vidjeti kako JavaScript engine reaguje kada ne moze pronaci metodu u samom objektu, niti u prototipnom lancu objekta. Objekat u ovom slucaju kreiram sa Object
konstruktorom jer on u Object.prototype
nema metodu za nizove.
let obj = new Object({1: 1, 2: 2, 3: 3, 4: 4});
obj.at(2); // obj.at is not a function
Ako se vratimo na sliku, strelice od objekta do prototype
svojstva su dvosmjerne jer se ne radi o kopiji vec [[Prototype]]
ima referencu na prototype
konstruktora.
let arr = [1, 2, 3];
arr.__proto__.num = 3;
Array.prototype.num; // 3
console.log(arr.__proto__ === Array.prototype); // true
Objekat ne cuva metode prototipa. Ono sto on cuva su samo podaci poput stavki niza [1, 2, 3]
.
Lanac predefinisanih prototipa
Mozda ste primjetili da Array.prototype
objekat na kraju predefinisanih metoda i svojstava ima naveden i [[Prototype]]: Object
. To je zato sto svi oblici ugradjenih prototipa nasljedjuju prototip obicnog objekta i na ovaj nacin je on poslednji u prototipskom lancu nasljedjivanja.
let arr = [];
console.log(arr.__proto__); // Array.prototype
console.log(arr.__proto__.__proto__); // Object.prototype
console.log(arr.__proto__.__proto__.__proto__); // null
// ili
console.log(Array.prototype.__proto__); // Object.prototype
console.log(Array.prototype.__proto__.__proto__); // null
U ovom slucaju bi JavaScript engine prvo trazio metodu ili svojstvo u samom objektu, potom u Array.prototype
, pa u Object.prototype
i nakon toga dobijamo gresku ukoliko ga ne nadje negdje u lancu.
Sta se nalazi u Array.__proto__
?Array
je konstruktor, a konstruktor je funkcija. Sve funkcije (zajedno sa konstruktorima) za svoj [[Prototype]]
imaju Function.prototype
.
Array.__proto__ === Function.prototype; // true
Funkcija je objekat i ona ima referencu na Object.prototype
.
Array.__proto__.__proto__ === Object.prototype; // true
Sada kada znamo kako pristupiti globalnim prototipima, napomenucu da treba izbjegavati mijenjanje izvornih prototipa da bi izbjegli konflikte usljed imenovanja. Medjutim, modifikacija metoda prototipa moze biti korisna za polifilovanje (kreiranje zamjenske metode koja postoji u JavaScript specifikaciji u pretrazivacu koji je jos nije implementirao).
if (!Array.prototype.join) {
Array.prototype.join = function() {...}
}
Kreiranje naseg prototipskog nasljedjivanja
Recimo da imamo konstruktor Oblik, ali ga zelimo prosiriti i iz njega izvesti nekoliko novih konstruktora raznih oblika:
function Oblik() {}
Oblik.prototype.oboji = function() {};
function Kocka() {}
Kocka.prototype = Object.create(Oblik.prototype);
// Sada je property svojstvo Kocka = Oblik.prototype
// i oboji() metoda ce biti dostupna svim objektima Kocke
Kocka.prototype.izracunajDimenzije = function() {}
let kocka = new Kocka();
Medjutim postoje ne tako ceste situacije kada zelimo novi objekat kreirati dinamicno, kada nam tip objekta nije unaprijed poznat ili zelimo kreirati objekat nekog tipa koji ce biti odredjen za vrijeme izvrsavanja koda. Tada new Kocka()
nije opcija. constructor
svojstvo u prototipu nam omogucuje da kreiramo novi objekat tako sto cemo koristiti postojeci objekat: new kocka.constructor()
.
Ali postoji mali problem ako dopisemo sledece
// ...
let kocka = new Kocka();
let kockica = new kocka.constructor();
kockica
ne nasljedjuje Kocku, vec Oblik. Zasto?
Stvar je u tome kako smo izvrsili nasljedjivanje.Kocka.prototype = Object.create(Oblik.prototype)
linija koda je potpuno zamjenila sva prethodna svojstva koja su zivjela u prototype
-u Kocke, a predefinisani su bili constructor
i [[Prototype]]
. Prvo constructor
svojstvo u lancu ce biti ono od konstruktora Oblik
i na njemu ce kljucna rijec new
kreirati novi objekat. Ispod je vrijednost kocka
objekta u konzoli console.log(kocka)
:
× Kocka {} × [[Prototype]]: Oblik ~ nedostaje: constructor: ƒ Kocka()~ > izracunajDimenzije: ƒ () × [[Prototype]]: Object > oboji: ƒ () > constructor: ƒ Oblik() > [[Prototype]]: Object
Zato je dobra praksa resetovati konstruktor svojstvo nakon nasljedjivanja druge konstruktor funkcije, ovako:
Kocka.prototype = Object.create(Oblik.prototype);
// Ispod resetujemo konstruktor i dodajemo metode
// koje pripadaju Kocka prototipu
Kocka.prototype.constructor = Kocka;
Kocka.prototype.izracunajDimenzije = function() {}
Sada ce pozivanje new kocka.constructor()
kreirati objekat konstruktorom Kocka
.
Mozemo napisati funkciju koja ce nas spasiti ponavljanja istih linija koda kada god zelimo prosiriti neki konstruktor:
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
}
extend(Kocka, Oblik);
Pozivanje roditeljskog konstruktora
S obzirom da na objekat uticu nasljedjeni prototipi, trebalo bi da naslijedi i clanove, ali se to u ovom slucaju ne desava automatski. Pozvacemo roditeljsku funkciju sa Oblik.call(this, argument)
da bi bila svjesna objekta na kom treba dodati clanove.
function Oblik(boja) {
this.boja = boja;
}
function Kocka(stranicaA, boja) {
Oblik.call(this, boja);
this.stranicaA = stranicaA;
}
Kocka.prototype = Object.create(Oblik.prototype);
Kocka.prototype.constructor = Kocka;
let kocka = new Kocka(50, 'crvena');
// kocka = { stranicaA: 50, boja: crvena }
.call()
metoda poziva funkciju sa datom this
vrijednosti i argumentima. Proslijedili smo joj novi objekat kao this
. Da smo pozvali funkciju sa Oblik(boja)
, this
bi u funkciji pokazivao na globalni objekat (window
u pretrazivacu), sto znaci da bi svojstvo boje bilo dodjeljeno globalnom objektu: window.boja = 'crvena'
.
Sintaksa: fn.call(thisObj, arg, /*...,*/, argN)
.
Ali, ako ne zelimo dodavati clanove nekih klasa direktno u objekat, vec u prototip, tada prototipu mozemo dodjeliti novu instancu Oblik konstruktor Kocka.prototype = new Oblik();
function Oblik() {
this.boja = 'crna';
}
Oblik.prototype.nacrtaj = function() {
console.log(this.boja + ' ' + this.constructor.name);
}
function Kocka(stranicaA) {
this.stranicaA = stranicaA;
}
Kocka.prototype = new Oblik(); // ***
Kocka.prototype.constructor = Kocka;
let kocka = new Kocka(50);
kocka.nacrtaj(); // crna kocka
U konzoli kocka
izgleda ovako:
× Kocka {} stranicaA: 50 × [[Prototype]]: Oblik boja: "crna" > constructor: ƒ Kocka(stranicaA) × [[Prototype]]: Object > nacrtaj: ƒ () > constructor: ƒ Oblik() > [[Prototype]]: Object
Rekreiranje new
operatora
Da bi bolje razumjeli kako new
radi, pravicemo se da ne postoji i rekreirati njegove korake. Pravimo funkciju koja:
- kreira prazan objekat na koji
this
pokazuje - novom objektu definise
[[Prototype]]
- nastanjuje novi objekat svojstvima
this.property = value
- vraca novi objekat
// konstruktor
function Person(name) {
this.name = name;
}
Person.prototype.talk = function() {
console.log('My name is ' + this.name);
};
// Funkcija imitira new
function newF(constructor, ...args) {
let obj = {};
Object.setPrototypeOf(obj, constructor.prototype);
constructor.apply(obj, args);
return obj;
}
var person1 = newF(Person, 'Ike');
// person1 = { name: 'Ike', __proto__: Person.prototype }
var person2 = new Person('Vana');
// person1 = { name: 'Vana', __proto__: Person.prototype }
person1.talk(); // My name is Ike
person2.talk(); // My name is Vana
Posudjivanje metode od prototipa:
Ispod zelimo metodu niza .join()
primjeniti na objekat koji nije niz.
function person() {
console.log(arguments.join(' '));
// arguments.join is not a function
console.log(arguments.__proto__ === Object.prototype); // true
}
person('Ike', 'Barlowe');
arguments
objekat sam po sebi je samo array-like, sto znaci da nije pravi niz (array) vec lici na njega jer ima indekse i length
svojstvo. Po kreiranju za [[Prototype]]
nasljedjuje Object.prototype
i u svom prototipskom lancu nema metode koje rade sa nizovima.
Iz ovog razloga arguments.join(' ')
izaziva gresku arguments.join is not a function
. Medjutim, vecini metoda iz Array.prototype
su samo potrebne vrijednosti sa indexima i svojstvo length
, ne i da se radi o pravim nizovima. Problem je u tome sto arguments
objekat nije kreiran kao niz i za prototip nije naslijedio Array.prototype
od konstruktora. Prema tome je dovoljno da arguments
objektu join()
metodu ucinimo dostupnom.
person('Ike', 'Barlowe');
function person() {
arguments.__proto__ = Array.prototype;
// Arguments [[Prototype]] sada pokazuje na Array.prototype
console.log(arguments.join(' ')); // Ike Barlowe
console.dir(arguments);
}
Ispis arguments
objekta u konzoli:
× Arguments(2) 0: "Ike" 1: "Barlowe" > callee: ƒ person() length: 2 > Symbol(Symbol.iterator): ƒ values() × [[Prototype]]: Array(0) > at: ƒ at() > concat: ƒ concat() ...
Umjesto modifikovanja prototipa objekta, moguce je samo uvesti potrebnu metodu u objekat.
function person() {
arguments.join = Array.prototype.join
...
× Arguments(2) 0: "Ike" 1: "Barlowe" > join: ƒ join() > callee: ƒ person() length: 2 > Symbol(Symbol.iterator): ƒ values() × [[Prototype]]: Object > constructor: ƒ Object() > hasOwnProperty: ƒ hasOwnProperty() ...
Prototip je i dalje ostao Object.prototype
, a join()
metoda radi.
Izracunata imena svojstava
U ovakvim funkcijama mozemo primjeniti jednu od super moci uglastih zagrada []
. One pruzaju opciju da definisemo ime svojstva racunanjem (nije ograniceno na klase i konstruktore, isto se moze uraditi direktno u objektu).
function Konstruktor(key, value) {
this['Korisnikovo ' + key] = value;
}
let obj = new Konstruktor('Ime', 'Nina');
console.log(obj); // {Korisnokovo Ime: 'Nina'}
Gubljenje this
i funkcija strelice
Jos jedan trik koji se moze izvesti u konstruktorima, klasama, ali ne i direktno u objektima.
Ukoliko neku metodu dodjelimo varijabli ona postaje nezavisna od objekta, zaboravlja na njegova ostala svojstva i this
se gubi prilikom pozivanja takve funkcije. Ovo se cesto dogadja kada metodu proslijedimo funkciji kao argument.
function Konstruktor(fraza) {
this.fraza = fraza;
this.ispisiFrazu = function() {
console.log('Nova fraza: ' + this.fraza);
};
}
let obj = new Konstruktor('Dobar dan.');
let metoda = obj.ispisiFrazu;
metoda(); // Nova fraza: undefined
Pored poznatih rjesenja poput omotavanja metode u funkciju, koristenja bind
metode, mozemo koristiti funciju strelice (arrow function () => {}
).
// Omotavanje metode u funkciju.
let metoda = function() {
obj.ispisiFrazu();
};
metoda(); // Dobar dan
// Bind metoda.
let metoda = obj.ispisiFrazu.bind(obj);
metoda(); // Dobar dan
// Definisanje metode sa funkcijom strelice:
function Konstruktor(fraza) {
this.fraza = fraza;
this.ispisiFrazu = () => {
console.log('Nova fraza: ' + this.fraza);
};
}
let obj = new Konstruktor('Dobar dan.');
let metoda = obj.ispisiFrazu;
metoda(); // Nova fraza: Dobar dan
Ovo radi jer je vrijednost this
u funkciji strelice odredjena na osnovu vanjskog leksickog opsega. Funkcija strelice nema svoj this
kao obicna funkcija. Ovaj isti trik ne radi u objektima zbog leksickog opsega za this
.
let obj = {
fraza: 'Dobar dan',
ispisiFrazu: () => {
console.log('Nova fraza: ' + this.fraza);
}
};
let metoda = obj.ispisiFrazu;
metoda(); // Nova fraza: undefined
Buduci da objekat ne stvara novi leksicki opseg, funkcija strelice ce traziti vrijednost za this
izvan objekta.
let obj = {
vrijednostThis: this
};
console.log(obj); // {vrijednostThis: Window}
this
pokazuje na Window
globalni objekat u kom ne postoji svojstvo fraza
i za rezultat JavaScript engine vraca vrijednost undefined
.