Přejít k navigační liště

Zdroják » JavaScript » Kopírování/klonování objektů v JS

Kopírování/klonování objektů v JS

Články JavaScript

Jak správně a úspěšně zkopírovat objekt v JavaScriptu. Popíšeme různé metody a rozdíly mezi nimi.

Text vyšel původně na webu autora.

V JS dochází při kopírování objektů k trochu odlišné situaci než při kopírování obsahu proměnných.

Je-li cílem zkopírovat obsah proměnné prvni do proměnné druhy, jde to provést následovně (živá ukázka):

var prvni = 'hodnota'
var druhy = prvni

druhy = 'jinaHodnota'

console.log(prvni) // hodnota
console.log(druhy) // jinaHodnota

Stejným způsob způsobem se může nabízet zkopírovat/naklonovat objekt.

var prvni = {
    vlastnost: 'hodnota'
}

var druhy = prvni

console.log(prvni) // {"vlastnost":"hodnota"}
console.log(druhy) // {"vlastnost":"hodnota"}

Na první pohled to vypadá funkčně. Problém ale nastává, když se změní nějaká vlastnost zkopírovaného objektu (živá ukázka):

var prvni = {
    vlastnost: 'hodnota'
}

var druhy = prvni

druhy.vlastnost = 'jinaHodnota'

console.log(prvni) // {"vlastnost":"jinaHodnota"}
console.log(druhy) // {"vlastnost":"jinaHodnota"}

Jak je vidět z výstupu JS konsole, oba objekty jsou stejné. Proč? Tímto způsobem se nekopíruje objekt, ale jen se na něj vytváří reference/odkaz. Nepochopení tohoto principu vede ke značným problémům.

...Spread operátor

Řešení je použít tzv. spread operátor – tři tečky bezprostředně před názvem proměnné/objektu (živá ukázka):

var prvni = {
    vlastnost: 'hodnota'
}

var druhy = { ...prvni }

druhy.vlastnost = 'jinaHodnota'

console.log(prvni) // {"vlastnost":"hodnota"}
console.log(druhy) // {"vlastnost":"jinaHodnota"}

Totéž jde zapsat i zkráceně jako:

var prvni = {
    vlastnost: 'hodnota'
}

var druhy = { ...prvni, ...{ vlastnost: 'jinaHodnota' } }

Tedy při přiřazení rovnou změnit nějakou vlastnost kopírovaného objektu.

Spread operátor (v překladu z angličtiny něco jako rozložit/rozprostřít) byl standardizován až v roce 2018 v ECMAScriptu 2018 (zkráceně označovaném jako ES2018 nebo ES9). Nefunguje tedy ve starších prohlížečích.

Nejen z tohoto důvodu je občas možné vidět věci jako:

var druhy = JSON.parse(JSON.stringify(prvni))

Funguje to trochu jinak než třítečkový operátor, ale v tomto případě to účel plní stejně. Živá ukázka

Vzhledem k tomu, že to nejprve převádí objekt na řetězec a následně parsuje zpět na objekt, není to výkonově úplně nejlepší. Spíš nouzové řešení.

Další způsob je použít Object.assign. Ten byl standardizován dříve než ...spread operátor, takže v případě psaní JS kódu, který se už nekompiluje nástrojem typu Babel, může dávat větší smysl používat toto řešení (živá ukázka):

var druhy = Object.assign({}, prvni)

Hluboké a mělké klonování

Při klonování objektů je třeba rozlišovat tzv. hluboké klonování (deep clone) a mělké (shallow clone).

Spread operátor ... dělá právě to mělké.

To se projeví tak, že změna hodnoty o úroveň níž (v příkladu dalsiVlastnost) se projeví i u klonovaného objektu (živá ukázka):

var prvni = {
    vlastnost: 'hodnota',
    dalsiVlastnost: {
        text: 'ahoj'
    }
}

var druhy = { ...prvni }
druhy.vlastnost = 'jinaHodnota'
druhy.dalsiVlastnost.text = 'fytopuf'

console.log(prvni) // {"vlastnost":"hodnota","dalsiVlastnost":{"text":"fytopuf"}}
console.log(druhy) // {"vlastnost":"jinaHodnota","dalsiVlastnost":{"text":"fytopuf"}}

 

Tedy první úroveň je naklonovaná, ale hlouběji už je jen reference na původní objekt.

Mělkou kopii vytváří i konstrukce Object.assign.

Co s tím?

Asi nejjednodušší řešení bez používání cizích knihoven je již výše zmíněný JSON.parse(JSON.stringify(objekt)) (živá ukázka).

Není to ale z výše popsaných důvodů úplně čisté řešení. Další problém je v tom, že se hodí jen pro klonování primitivních datových typů jako je řetězec (String), číslo (Number) nebo true/false (Boolean).

Pokud bude v objektu třeba funkce, tento převod tam a zase zpět nepřežije.

Knihovny pro deep copy

Jako best-practice považuji použít např. funkci cloneDeep z populární knihovny Lodash (živá ukázka):

var prvni = {
    vlastnost: 'hodnota',
    dalsiVlastnost: {
        text: 'ahoj'
    }
}

var druhy = _.cloneDeepWith(prvni)

druhy.vlastnost = 'jinaHodnota'
druhy.dalsiVlastnost.text = 'fytopuf'

console.log(prvni) // {"vlastnost":"hodnota","dalsiVlastnost":{"text":"ahoj"}}
console.log(druhy) // {"vlastnost":"jinaHodnota","dalsiVlastnost":{"text":"fytopuf"}}

I kdysi populární knihovna jQuery má funkci extend, která umí hloubkově klonovat objekty (živá ukázka):

var druhy = $.extend(true, {}, prvni);

Odkazy jinam

Komentáře

Odebírat
Upozornit na
guest
11 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
Zobrazit všechny komentáře
Jakub Vrána

Druhý spread u ukázky zkráceného zápisu je zbytečný:

var druhy = { ...prvni, ...{ vlastnost: 'jinaHodnota' } }

Lze to napsat takto:

var druhy = { ...prvni, vlastnost: 'jinaHodnota' }
Vítězslav Gazda

Fuj, strašný jazyk … === atd.

Mlocik97

JavaScript je v pohode ako každý iný jazyk, samozrejme má aj on svoje muchy a nechutnosti, ale tak ukáž mi jazyk, ktorý nemá.

L.

Co je na === fuj? Operátor jako každý jiný, to je v pohodě. Horší jsou třeba jazyky, kde neexistují blokové závorky a tak se bloky určují indentací, to je teprve správný humus.

dor

Je pravda, že samotný operátor === není fuj. Fuj jsou důvody, proč musí existovat. Protože == je v js fuj. Žít se s tím sice dá, ale já to fuj chápu.

Mlocik97

@dor a jeden fuj operátor znamená že celý JavaScript je strašný? Tak to potom je strašný absolútne každý programovací jazyk.

dor

To určitě ne. Jak jsem psal: fuj chápu, ale žít se s tím dá. Nemůžou být všechny jazyky tak skvělé, jako C#. :-D

Mlocik97

„tak skvelé ako …“ je typický názor fanatika, a nemá nič s kvalitou programovacieho jazyka.

dor

Zkus to přečíst ještě jednou a až budeš číst poslední tři znaky, polož hlavu na levé rameno. A hlavně zhluboka dýchej. Jsou to jenom nástroje. ;)

Mlocik97

Sry, ale keď napíšeš smajlík za každú vetu a pak sa na to odvolávaš, tak to není irónia, sarkazmus ani nič podobné ale len psychická poruha, a pripomína mi to typický postoj Rikiho Fridricha čo si z toho robil srandu a naprogramoval Mimi Tourette https://www.youtube.com/watch?v=aTucFxlZmTA

Martin Hassman

Jak to tu pár dní sleduji, vypadá to, že téma diskuse již bylo vyčerpáno a měla být už dávno ukončena. Tak ji dnes uzavírám.

Stack Overflow spouští AI Assist: nový nástroj pro moderní vývojáře

Stack Overflow představil AI Assist, nástroj, který propojuje generativní AI s rozsáhlou databází ověřených znalostí komunity. Platforma, která byla více než 18 let klíčovým zdrojem řešení pro vývojáře po celém světě, tím reaguje na změny ve způsobu práce s informacemi. Cílem AI Assist je zrychlit hledání odpovědí, zvýšit jejich spolehlivost a nabídnout kontext, který pomáhá lépe porozumět problému i řešení. Tento krok odráží trend, kdy se vývojáři čím dál více obracejí na nástroje, které dokážou kombinovat rychlost AI s ověřenými znalostmi komunity. AI Assist se tak stává mostem mezi tradičním Q&A formátem a moderními interaktivními asistenty.

Austrálie vs. sociální sítě: ochrana dětí nebo absurdní regulace?

Různé
Komentáře: 1
Austrálie chystá razantní krok – od prosince 2025 budou děti mladší 16 let muset opustit sociální sítě. Ministryně komunikací Anika Wells ujistila, že vláda se nenechá zastrašit technologickými giganty. Zákon má chránit mladé uživatele před negativními dopady online světa, ale kritici varují, že může vést spíš k obcházení pravidel než k reálné ochraně.

Tor přechází na nový šifrovací algoritmus CGO

Různé
Komentáře: 0
Tor Project představil nový šifrovací algoritmus Counter Galois Onion (CGO), který nahrazuje zastaralý systém tor1. CGO přináší výrazně lepší ochranu proti manipulaci a sledování, zajišťuje forward secrecy a silnou autentizaci přenášených dat, přičemž zachovává vysokou výkonnost a nízkou režii provozu.