Kopiranje i kloniranje objekata u JavaScriptu. Jedan 'novi' trik za pomoć svima nama.
Tags: webdev, javascript, programming
Published: 2023-03-16 13:52:56

Kada se radi sa JavaScriptom, u bilo kom okruženju, jedan od prijedloga ili nepisanih pravila je da se ne mutira/mijenja originalni objekat. Najčešće je u pitanju npr. neki objekat koji je došao sa nekog endpointa, ali ne nužno. Može biti u pitanju bilo koji objekat, sa bilo kog izvora. Razlog je jednostavno u tome što je moguće da će neki drugi dio aplikacije odnosno programa koristiti isti taj objekat i nije dobro da se on mijenja.

Rješenje za takvo nešto je da se, naizgled jednostavno, napravi kopija tog objekta i da se mutira/mijenja ta kopija. Ali ovdje se radi o JavaScriptu, objekti su referentni tipovi, te stoga njihovo kopiranje nije tako jednostavna tema.

### Šta ćete ovdje pročitati?

- **Kako se objekti kopiraju u JavaScriptu?**
- **Kombinacija metoda `JSON.stringify` i `JSON.parse`.**
- **Spread `(...)` operator.**
- **Nova metoda `structuredClone()`.**

---

#### Kako se objekti kopiraju u Javascriptu?
Objekti u JavaScriptu su referentni tipovi podataka (za razliku od primitivnih; više o tipovima možete pričitati u [ovom](https://dev.to/bslaven/tipovi-u-javascriptu-eef) tekstu). Jedna od karakteristika referentnih tipova je da se oni kopiraju po referenci. Odnosno kada kopiramo jedan objekat u drugi, to znači da nismo kopirali njegov sadržaj u drugi objekat nego njegovu lokaciju u memoriji.


![copy an object to another one in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlw534yh19nca7sljogo.png)

Ovdje smo kopirali objekat `osoba1` u `osoba2` ali smo kopirali njegovu referencu na lokaciju u memoriji. Ova dva objekta dijele sada tu referencu. Zato ako pokušamo da promijenimo jedan od ovih objekata automatski ćemo promijeniti i drugi. Zato što smo promijenili objekat u memoriji, a oba ova objekta upućuju na istu lokaciju u memoriji.

![change value in copied object in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aimxfr35d88zwcy4jrek.png)

Promjena godina u objektu `osoba2` je promijenila godine i u objektu `osoba1`.

Dakle, u pokušaju da se pridržavamo pravila da ne mutiramo originalni objekat pravljenje obične kopije tog objekta neće funkcionisati. Zato je potrebno napraviti **klon** tog objekta, objekat koji ima sva svojstva kao i originalni objekat, ali koji ne dijeli njegovu referencu.

---

#### Kombinacija metoda `JSON.stringify` i `JSON.parse`

Jedno od rješenja za pravljenje klon objekta u JavaScriptu je kombinovanje metoda `JSON.stringify` i `JSON.parse`.
`JSON.stringify` metoda u JavaScriptu će uzeti objekat i pretvoriti ga u obični tekst, a `JSON.parse` metoda će ga vratiti iz teksta u obični JavaScript objekat. Koristićemo naš postojeći objekat `osoba1`.

![parse and stringify to clone an object in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxq0a4jmhrtih2ejf8zv.png)

Ova kombinacija metoda funkcioniše za neke osnovne potrebe. Međutim, problem kod ovog pristupa je što ne funkcioniše sa npr. datumima.

![parsing date from string in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwsig2kuzo1vgwufslsx.png)

Kao što možemo viditi iz ovog primjera primjena metoda `stringify` i `parse` na datumu je promijenila taj datum. Naime, `stringify` metoda je pretvorila datum u string u punom formatu, ali `parse` metoda nije vratila taj string u puni datum, nego u ISO format.

Dakle, `JSON.stringify` metoda je dobra kada radimo sa primitivnim tipovima kao što su `string`, 'number' ili 'boolean'.
Evo nekliko primjera čudnog ponašanja:

![JSON.stringify in JavaScript bad behaviour](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8un4z37kwy6malbmtel0.png)

Kada `JSON.stringify` ne zna šta da radi sa nekim podacima može se lako dogoditi ili da izgubimo neke podatke ili da dobijemo nešto potpuno drugačije.

---

#### Spread `(...)` operator

Sa ES6 standardom JavaScripta došao je jedan odličan operator koji se može koristiti za kloniranje objekata. To je 'spread' (...) operator. Kao što se vidi on izgleda kao obične tri tačke. Koristi se tako što se jednostavno postave tri tačke ispred objekta koji želimo da kopiramo i to dodamo kao vrijednost novom objektu.

![spread object into another object in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hwz1q3mbov998rcfihms.png)

Kao što se vidi iz osnovnog primjera moguće je koristiti i druge vrijednosti sa kojim smo imali problema u prethodnim primjerima. Vrijednosti su prenesene u drugi objekat onakve kakve su bile i u originalnom objektu. Datumi su u formatu koji smo imali i u originalnom objektu.

Naravno, ako promijenimo nešto u kloniranom objektu ta promjena se neće viditi u originalnom objektu.

![change key in object after spread in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/juo9kxyiqdwiim3bf1x4.png)

Međutim, spread operator ima jednu, ali prilično veliku manu kada je kloniranje objekata u pitanju. Ta mana je što on pravi samo takozvane plitke kopije objekata. To znači da, ako neki objekat ima svojstvo koje je opet objekat, samo referenca tog 'unutrašnjeg' objekta će biti kopirana. Samim tim bilo koja promjena u tom unutrašnjem objektu će se odraziti i u unutrašnjem objektu originalnog objekta.

Ovo je veoma važna razlika jer ćete u stvarnosti često raditi sa objektima koji imaju više nivoa.


![spread creating a shallow copy in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gj4da1n7ob0wq4q0n2to.png)

U ovom primjeru se vidi da objekat `osoba1` ima svojstvo `adresa` koji je i sam objekat, sa svojim svojstvima. Kada smo klonirali objekat `osoba1`, objekat `adresa` nije kloniran nego je samo kopirana njegova referenca. Zato, kada smo promijenili broj na adresi u kloniranom objektu, promijenili smo i broj na adresi u originalnom objektu.

S obzirom da je spread operator lično moj omiljeni način za kloniranje objekata, uvijek moram biti svjestan njegovog jednog velikog nedostatka i misliti na to koju će strukturu imati moj originalni objekat.

---

#### Nova metoda `structuredClone()`

Nešto što se dugo čekalo u JavaScriptu je metoda `structured Clone`. Ova metoda je ugrađena u JavaScript u svim okruženjima. Koristi se tako što joj se kao parametar dodijeli objekat koji želimo da kopiramo i to se dodijeli kao vrijednost novom objekutu.


![structuredClone example in JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1kg62jbwa89zvfy5fjjl.png)

Kao što se vidi iz primjera `structuredClone` može da se koristi sa specijalnim podacima kao što su `Date`, `Set`, sa funkcijama.

Metoda `structuredClone` automatski pravi duboki klon datog objekta, bez obzira koliko nivoa je dubog objekat.

Svakako postoje i drugi načini koji se mogu koristiti za kopiranje objekata.
- Može se koristiti `Object.assign()` pristup. Ovo međutim takođe pravi plitku kopiju objekta.
- Može se koristiti neka od biblioteka koje imaju rješenje za ovo. Jedna od njih je [lodash](https://lodash.com/). Ona ima svoju metodu `cloneDeep` koja može da posluži za kloniranje. Jedini problem je što morate uvesti cijelu biblioteku samo da bi dobili jednu metodu. Ako ne koristite ništa više iz biblioteke `lodash` postoje bolja rješenja.