Uvod
Object() je funkcija koja se može smatrati “majkom” svih objekata. Svi objekti u JavaScriptu su nastali od ove funkcije. Kreiranje objekata je moguće na više načina ali naredni način najbolje objašnjava vezu izmedju konstruktorske funkcije i objekta:
1 |
var nekiObjekat = new Object(); |
Konstruktorska funkcija ima brojne metode i dva svojstva: “Object.length” (veoma čudno svojstvo koje uvek vraća 1) i “Object.prototype”
OBJAŠNJENJE:
Funkcije u javascriptu su “First class citizen”, stoga podržavaju sve operacije dostupne drugim tipovima. Izmedju ostalih tipova funkcije podržavaju i karakteristike objekata, pa stoga funkcije kao i objekti imaju svoja svojstva i metode. Jedno od najbitnijih karakteristika konstruktorske funkcije je svojstvo Object.prototype, preko koga konstruktorska funkcija prosledjuje osnovna svojstva i metede svim objektima u JavaScript-u.
“Object.prototype” svojstvo konstruktorske f-ije
“Object.prototype” nema neko “pametno” ime, već generičko, nastalo na osnovu pripadnosti funkciji (svojstvo konstruktorske funkcije Object()). Ovo svojstvo konstruktorske funkcije (tipa “objekat”) je zaduženo da prosledjuje metode i svojstva svim novo generisanim objektima. Ovaj objekat delegira sva svoja svojstva i metode novo kreiranim objektima, pa su im tako dostupna od samoga starta. Object.prototype se nalazi na kraju lanca nasledjivanja i sva svojstva i metode koje poseduje su dostupne svakome objektu javascripta.
- SVOJSTVA:
- .constructor
- __proto__
- METODE:
- hasOwnProperty()
- isPrototypeOf()
- propertyIsEnumerable()
- toString()
- valueOf()
Svojstva ovog objekta su specifična jer njihovi deskriptori svojstva imaju negativne vrednosti (writable=no; enumerable=no; configurable=no), a pošto svojojstva nisu enumerable, to znači da nisu dostupna kroz “for…in” loop. Karakteristike svojstava ovoga objekta su sledeće:
Property attributes of Object.prototype | |
---|---|
Writable | no |
Enumerable | no |
Configurable | no |
Ova svojstva i metode “Object.prototype delegira i objektima koji nastaju nasledjivanjem tj. svim objektima duž lanca nasledjivanja. Object.prototype se uvek nalazi na kraju prototipskog lanca nasledjivanja.
Svojstva “Object.prototype” objekta
Object.prototype.constructor
Ovo svojstvo ukazuje na konstruktorsku funkciju od koje je nastao prvobitni objekat, a ukoliko objekat nema svoju “ličnu” konstruktorsku funkciju (npr. objekt literal {}, nastao korišćenjem ključne reči new…), onda javascript engine svojstvo .constructor traži duž lanca nasledjivanja sve dok je ne nadje.
1 2 3 4 5 6 7 8 |
var o = {}; o.constructor === Object; // true var a = []; a.constructor === Array; // true var n = new Number(3); n.constructor === Number; // true |
Object.prototype.__proto__
Ovo svojstvo (tzv. “dunder proto”, skraćeno od “double underscore proto”) ukazuje na roditeljski objekat od koga naš objekat nasledjuje svojstva i metode. Sa ovim svojstvom možemo čak i da predefinišemo objekat od koga želimo da nasledjujemo, mada generalno trebamo izbegavati korišćenje ovog svojstva, jer je veoma sporo, a pored toga nije po ES standardu iako je prihvaćeno u svim browserima (izuzev IE<11). Ukoliko baš želimo da koristimo ovako svojstvo, bolje je da koristimo svojstvo getPrototypeOf() koje je prihvaćeno ES2015 standardom, a ima iste karakteristike kao __proto__.
1 |
nekiObjekat.__proto__ == Object.getPrototypeOf(nekiObjekat) // Vraća roditeljski prototype objekat |
Ovo svojstvo može da se koristi i ulančano, pa tako može da se dodje do bilo kog prototype objekta duž celog lanca nasledjivanja:
1 |
nekiObjekat.__proto__.__proto__ // Vraća prototype objekat dubine dva nivoa |
Koristeći ovo nestandardizovano svojstvo možemo da od drugog objekta napravimo naš prototype objekat, tako što mu jednostavno dodelimo drugi objekat.
Primer
1 2 3 4 5 6 7 |
var objekat1 = { a: 1, b: 2 }; var nekiObjekat = {}; nekiObjekat.__proto__ = objekat1; console.log(nekiObjekat.a); // Vraća: "1" |
Metode Object.prototype objekta
Object.prototype.isPrototypeOf()
Ova metoda proverava da li objekat postoji u nečijem lancu nasledjivanja.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function Foo() {} function Bar() {} function Baz() {} Bar.prototype = Object.create(Foo.prototype); Baz.prototype = Object.create(Bar.prototype); var baz = new Baz(); console.log(Baz.prototype.isPrototypeOf(baz)); // true console.log(Bar.prototype.isPrototypeOf(baz)); // true console.log(Foo.prototype.isPrototypeOf(baz)); // true console.log(Object.prototype.isPrototypeOf(baz)); // true |
Object.prototype.hasOwnProperty()
Metodom “hasOwnPropery()” se ispituje da li odredjeni objekat (bez objekata u protoytpe lancu) ima neko svojstvo, vraća boolean vrednost. Ovo svojstvo će da proveri čak i non-enumerable lična svojstva objekta.
1 2 |
var nekiObjekat = {a:2}; nekiObjekat.hasOwnProperty("a"); // vraća true |
NAPOMENA:
Pored prethodnog načina često se može videti sledeći pristupi ovom problemu:
a) Operator “in”
Za ispitivanje svih objekta u prototype lancu se koristi operator in. Sa ovim operatorom se ispituje da li sam objekat ima neko svojstvo, ali se takodje proveravaju i svi objekti koji delegiraju svojstva u prototype lancu.
1 2 3 |
if ("svojstvo" in nekiObjekt){ //neki kod } |
b) operator !==
1 2 3 4 5 |
if (obj.svojstvo !== undefined) { return true; } else { return false; } |
Ovaj način vraća isti rezultat kao operator “in” (sva svojstva u prototype lancu”), medjutim ovakav pristup se ne preporučuje jer najsporiji.
Object.prototype.propertyIsEnumerable()
Metodom “propertyIsEnumerable()” ispituje da li je deskriptor svojstva enumerable setovano na vrednost true, tj da li je svojstvo iterativno i vidljivo kroz iteracije svojstava.
1 |
nekiObjekat.propertyIsEnumerable(svojstvo); |
Primer
1 2 3 4 |
var o = {}; Object.defineProperty(o, "foo", { enumerable: false }); console.log(o.hasOwnProperty('foo')); // "true" console.log(o.propertyIsEnumerable('foo')); // "false" |
Object.prototype.toString()
Ova metoda vraća string reprezentaciju objekta.
1 2 3 4 5 6 7 8 9 |
function Dog(name, breed, color, sex) { this.name = name; this.breed = breed; this.color = color; this.sex = sex; } theDog = new Dog('Gabby', 'Lab', 'chocolate', 'female'); theDog.toString(); // Vraća: [object Object] |
Pošto su i nativni objekti (String, Array, Number…) takodje objekti, onda se i na njih može primeniti mehanizam delegiranog nasledjivanja, pa čak i dodavanje novih svojstava početnom prototype objektu:
1 2 3 |
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ‘’); }; |
Sada možemo da koristimo metodu trim() na svim stringovima:
1 |
" foo bar ".trim(); // Vraća očisceni string "foo bar" |
Mana ovog pristupa je ta što novije verzije JavaScript-a mogu imati novu funkcionalnost pod baš tim nazivom “trim” pa bi naša metoda pregazila standardizovanu metodu. Ovaj problem bi mogao da se reši na sledeći način:
1 2 3 4 5 |
if(!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ‘’); }; } |
Metode konstruktorske funkcije
Object.create()
Sa metodom Object.create() se generiše objekat kome se odmah pri kreiranju definiše od kog objekta mu se delegiraju metode preko “prototype lanca” (ne dolazi do kopiranja svojstava!). Syntax-a za kreiranje objekat je:
1 |
Object.create(proto[, propertiesObject]) |
Metoda Object.create() u pozadini izvršava sledeće taskove:
- Kreira se novi objekat
- Novi objekat se povezuje sa objektom od koga je nastao i tako se ubacuje u lanac nasledjivanja, nakon čega može ima pristup njegovim delegiranim metodama
- Opciono može da definiše i nova svojstva objekta.
Više o ovome pročitajte u članku “1001 način kreiranja objekata”.
Object.assign()
Ovo svojstvo kopira sva svojstva jednog ili više objekata kod kojih je enumerable deskriptor svojstva setovan na true, a zatim vraća objekat.
Kloniranje objekata
1 2 3 |
var obj = { a: 1 }; var copy = Object.assign({}, obj); console.log(copy); // { a: 1 } |
NAPOMENA:
Ovo svojstvo kopira samo “lična svojstva objekta”, ne i svojstva duž prototipskog lanca nasledjivanja.
Spajanje više objekata u jedan
Ovo svojstvo može da se koristi i sa više objekata, stim što treba naglasiti da duplirana svojstva se ne prikazuju kao dva već kao jedno svojstvo.
1 2 3 4 5 6 |
var o1 = { a: 1, b: 1, c: 1 }; var o2 = { b: 2, c: 2 }; var o3 = { c: 3 }; var velikiObjekat = Object.assign({}, o1, o2, o3); console.log(velikiObjekat); // { a: 1, b: 2, c: 3 } |
Object.getPrototypeOf()
Sa ovom metodom dobijamo koji je prototype objekat odredjenog objekta.
1 2 3 |
var proto = {}; var obj = Object.create(proto); Object.getPrototypeOf(obj) === proto; // true |
Ova metoda nije nejbolje radila za vreme ES5 standarda medjutim poboljšana je sa ES2015 standardom.
Primer
1 2 3 4 |
Object.getPrototypeOf('foo'); // (ES5 code) Vraća TypeError: "foo" is not an object Object.getPrototypeOf('foo'); // (ES2015) Vraća korektnu vrednost - String.prototype |
Object.setPrototypeOf()
Ekstendovanje konstruktorske funkcije se vrše korišćenjem metode Object.create(), koja definiše prototype objekat jednog objekta durugom (pročitajte malo više o ovome u članku 1001 način kreiranja objekata u JS-u.
1 |
Constructor2.prototype = Object.create(Constructor1.prototype); |
Medjutim object literal nema svoju konstruktorsku funkciju pa je potrebno koritisti metodu Object.setPrototypeOf(), koja dodeljuje prototype objekat drugom objeku.
1 |
Object.setPrototypeOf(obj, prototype); |
NAPOMENA:
Ova metoda je zamena za nestandardizovanu metodu
Object.prototype.__proto__ (pročitajte više o njoj u članku “Prototipsko nasledjivanje”), koja nije bila preporučena za korišćenje jer je veoma spora, isto važi i za metodu Object.setPrototypeOf(), stoga je izbegavajte!
Primer
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var proto = { y: 2 }; var obj = { x: 10 }; Object.setPrototypeOf(obj, proto); proto.y = 20; proto.z = 40; if (console && console.log) { console.log(obj.x === 10); // Returns true console.log(obj.y === 20); // Returns true console.log(obj.z === 40); // Returns true } |
Object.defineProperty()
Metoda Object.defineProperty() definiše ili modifikuje svojstva objekta.
Primer
Ovo svojstvo može da se koristi pravljenje privatnih svojstva uz korišćenje specijalnih metoda tzv. “get-era” i “set-era”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var programer = {}; Object.defineProperty(programer, 'ime', { writable: true }) Object.defineProperty(programer, 'prezime', { writable: true }) function get_punoIme() { return this.ime + ' ' + this.prezime } function set_punoIme(novoIme) { var unesenoIme; unesenoIme = novoIme.trim().split(/\s+/); this.ime = unesenoIme['0'] || ''; this.prezime = unesenoIme['1'] || ''; } Object.defineProperty(programer, 'punoIme', { get: get_punoIme , set: set_punoIme , configurable: true , enumerable: true }); programer.punoIme="Pera Peric"; console.log(programer.punoIme); // Vraca: "Pera Peric" |
Sa ovakim mehanizmom, svaki put kada želimo da ostvarimo interakciju sa svojstvom “punoIme ostvarujemo ili sa “get_punoIme()” ili “set_punoIme()” metodom.
Object.is()
JS je “loosely typed” jezik, koji u odredjenim situacijama implicitno konvertuje tipove. Konvertovanje pri poredjenju sa “==” operatorom često daje neželjene rezultate.
1 2 3 |
0 == ' ' //true null == undefined //true [1] == true //true |
Stoga se često koristi “===”, mada i poredjenje sa ovim operatorom može da da nelogičan rezultat:
Primer
1 |
NaN === NaN //false |
Sve ove probleme rešava metoda Object.is() koja proverava da li su dva objekta jednaka, i to radi bez greške.
Primer
1 |
console.log(Object.is(NAN, NAN)); //Vraća TRUE |
Primer
Takodje rešava i menje poznate nedoslednosti JavaScripta i vraća odogovarjući odgovor, kao što su:
1 2 3 |
Object.is(0, -0); // Vraća: false Object.is(-0, -0); // Vraća: true Object.is(NaN, 0/0); // Vraća: true |
Pogledajte tablicu sa rezultatima poredjenja koristeći operatore ==, === i metodu Object.is():
Object.values
Kreira niz od svih vrednosti “enumerable” svojstva objekta (bez svojstva duž lanca nasledjivanja).
1 |
Object.values(obj) |
Primer
1 2 |
var nekiObjekat = { a: 'pera', b: 'mika', c: 'zika' }; console.log(Object.values(nekiObjekat)); // ['mika', 'zika', 'pera'] |
NAPOMENA:
Redosled članova je random isto kako kod for..in petlje!
Object.keys
Kreira niz od svih ključeva “enumerable” svojstva objekta (bez svojstva duž lanca nasledjivanja).
1 |
Object.keys(obj) |
Primer
1 2 |
var nekiObjekat = { a: 'pera', b: 'mika', c: 'zika' }; console.log(Object.keys(nekiObjekat)); // ['b', 'c', 'a'] |
NAPOMENA:
Redosled članova je random isto kako kod for..in petlje!
Object.entries
Kreira niz od svih [key, value] parova “enumerable” svojstva objekta (bez svojstva duž lanca nasledjivanja).
1 |
Object.entries(obj) |
Primer
1 2 |
var nekiObjekat = { a: 'pera', b: 'mika', c: 'zika' }; console.log(Object.entries(nekiObjekat)); // [['b','mika'], ['c','zika'], ['a','pera']] |
NAPOMENA:
Redosled članova je random isto kako kod for..in petlje!
Object.getOwnPropertyNames()
Ova metoda vraća niz svih ključeva objekta pa čak i onih svojstava kod kojih je deskriptor enumerable: false
1 2 |
var obj = { 0: 'a', 1: 'b', 2: 'c' }; console.log(Object.getOwnPropertyNames(obj).sort()); // Vraća ["0", "1", "2"] |
Za pregled svojstava objekta može da se koristi i petlja “for..in” koja prolazi kroz sva svojstva (koja imaju setovano enumerable: true) objekta i celog prototype lanca.
1 2 3 4 5 6 7 8 9 10 11 |
var obj = {a:1, b:2, c:3}; for (var prop in obj) { console.log("obj." + prop + " = " + obj[prop]); } // Output: ------------------- // "obj.a = 1" // "obj.b = 2" // "obj.c = 3" |
Object.getOwnPropertyDescriptor()
Deskriptore svojstva nekog objekta dobijamo koristeći metodu “Object.getOwnPropertyDescriptor()”:
Primer
1 2 3 4 5 6 7 8 9 10 |
var nekiObjekat = {a:2}; Object.getOwnPropertyDescriptor(nekiObjekat, "a"); // Vraća objekat sa atributima svojstva "a": { configurable : true enumerable : true value : 2 writable : true } |
Object.getOwnPropertyDescriptors()
Sa ovom metodom dobijamo sva “lična” (ne nasledjena) svojstva i njihove deskriptore svojstava:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
const obj = { [Symbol('foo')]: 123, get bar() { return 'abc' }, }; console.log(Object.getOwnPropertyDescriptors(obj)); // Output: // { [Symbol('foo')]: // { value: 123, // writable: true, // enumerable: true, // configurable: true }, // bar: // { get: [Function: bar], // set: undefined, // enumerable: true, // configurable: true } } |
Koristi se za kloniranje objekata
1 |
const clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); |
Object.PreventExtension()
Ako želimo da sprečimo dodavanje novih svojstva nekom objektu koristimo metodu “Object.PreventExtension()”
1 |
Object.preventExtension(nekiObjekat) |
Object.seal()
Metoda “Object.seal()” uzima objekat i za njega poziva prethodnu metodu preventExtension(), i takodje postavlja deskriptor configurable na false (=> configurable: false + sprečeno dodavanje novih svojstva). Rezultat ove metode je da se objektu ne mogu dodati nova svosjtva, kao i da se postojeća svojstva ne mogu izbrisati, niti da se mogu promeniti drugi deskriptori osim deskriptora “value”
Object.freeze()
Metoda “Object.freeze()” preuzima postojeći objekat, i za njega poziva metodu “Object.seal()” i svim svojstvima objekta menja vrednost descriptora “writable” na false (=> writable: false, configurable: false + sprečeno dodavanje novih svojstva). Ova metoda omogućuje najviši nivo nepromenjivosti objekta.
Object.prototype.__proto__ prva linija
ukazuje na na roditeljski – typo greska.
Mozete obrisati komentar posle
Sredjeno hvala!
Odlicno objasnjeno, hvala na trudu i na kvalitetnom materijalu.