JavaScript clone object (shallow spread, structuredClone, JSON)

Clone objects in JavaScript: shallow spread and Object.assign, for...in, Object.create + descriptors, JSON limits, structuredClone and DataCloneError, and circular refs. Runnable Node snippets with short output notes; pairs with classes and array copy guides.

Published

Updated

Read time 5 min read

Reviewed byDeepak Prasad

JavaScript clone object (shallow spread, structuredClone, JSON)

javascript clone object and js copy object workflows fall into shallow copies (new top-level object, shared nested references) and deep copies (nested data duplicated). For javascript copy object in modern engines, structuredClone is usually the best native deep clone; JSON.parse(JSON.stringify(x)) is still common but lossy. If you copy plain data from class instances, remember structuredClone does not preserve the original prototype chain.

Tested on: Node.js v20.18.2. A short note after each snippet describes what you should see in the console.


Quick reference

Shallow copies share nested references; structuredClone is the default deep choice when types are supported; JSON is a narrow, lossy subset.

Goal Approach
Shallow duplicate { ...obj } or Object.assign({}, obj)
Deep clone, JSON-safe plain data JSON.parse(JSON.stringify(obj)) (watch Date, undefined, functions, cycles)
Deep clone, modern default structuredClone(obj) when types fit the algorithm
Prototype + descriptors snapshot Object.create(Object.getPrototypeOf(x), Object.getOwnPropertyDescriptors(x))

Shallow copy with spread — clone object javascript

Spread { ...obj } is the most common clone object javascript pattern: enumerable own properties copy to a new object in one expression. Nested objects stay aliased—mutating a nested field shows up on both sides—while reassigned top-level keys diverge.

javascript
const a = { x: 1, n: { v: 0 } };
const b = { ...a };
b.n.v = 1;
b.x = 2;
console.log(a.n.v, b.n.v, a.x, b.x);
Output

You should see one line: 1 1 1 2.

Nested n is shared (both show 1); top-level x diverges (1 vs 2). Same behavior as other spread operator shallow merges.


Shallow copy with Object.assign

Object.assign({}, src) is another js clone object shallow path: it copies enumerable own properties onto a target object. Nested references behave like spread—k.z updates both views in the output below.

javascript
const o1 = { k: { z: 1 } };
const o2 = Object.assign({}, o1);
o2.k.z = 9;
console.log(o1.k.z, o2.k.z);
Output

You should see one line: 9 9.


for...in manual copy — object clone javascript

A for...in loop is the explicit object clone javascript style when you cannot rely on syntax sugar: copy each key into a fresh object. It is still shallow for nested values, and without Object.hasOwn (or hasOwnProperty checks) you may copy enumerable inherited keys—guard when cloning plain objects that sit on a busy prototype chain.

javascript
const originalObject = { type: "van", tyres: 4 };
const clonedObject = {};
for (const key in originalObject) {
  clonedObject[key] = originalObject[key];
}
console.log(JSON.stringify(clonedObject));
Output

You should see one line: {"type":"van","tyres":4}.


Object.create + property descriptors — js clone object with prototype

Object.create(Object.getPrototypeOf(src), Object.getOwnPropertyDescriptors(src)) preserves prototype linkage and property descriptors—useful when object clone javascript questions mention getters, setters, or non-enumerable fields. Values are still shallow: the series array below is one shared reference between originalObject and clonedObject unless you deep-clone that branch separately.

javascript
const originalObject = {
  type: "main",
  condition: false,
  series: [13, 17, 19],
  age: 30,
};
const clonedObject = Object.create(
  Object.getPrototypeOf(originalObject),
  Object.getOwnPropertyDescriptors(originalObject)
);
console.log(JSON.stringify(clonedObject));
Output

You should see one line: {"type":"main","condition":false,"series":[13,17,19],"age":30}.


Deep copy with JSON — lossy

JSON.parse(JSON.stringify(obj)) is a narrow javascript copy object hack: it only survives JSON-safe shapes. undefined keys disappear, Date values stringify, functions and symbols vanish, and circular graphs throw—pair this section with copying arrays when the same JSON caveat applies to collections.

javascript
const j = { a: 1, u: undefined, d: new Date(0) };
const jc = JSON.parse(JSON.stringify(j));
console.log(JSON.stringify(jc));
console.log(typeof jc.d, jc.d);
Output

You should see two lines: first a JSON string without u, then string and an ISO date string (not a Date instance).

undefined is omitted; Date becomes an ISO string, not a Date instance.


Deep copy with structuredClone — javascript clone

structuredClone is the native javascript clone answer for many plain data graphs: nested objects detach, Date, Map, and other structured-cloneable types round-trip, and circular references can be preserved (MDN: structuredClone). It throws DataCloneError for unsupported types such as functions.

javascript
const o = {
  d: new Date(0),
  n: { v: 1 },
  m: new Map([["k", 1]]),
};
const c = structuredClone(o);
c.n.v = 2;
console.log(o.n.v, c.n.v, c.d instanceof Date, c.m instanceof Map, c.m.get("k"));
Output

You should see one line: 1 2 true true 1.

Nested n is independent (1 vs 2); Date and Map survive cloning.


structuredClone and functions — DataCloneError

Functions are not structured-cloneable: wrapping them in an object and calling structuredClone fails fast with DataCloneError, which is what most js clone object guardrails should catch when user data includes callbacks.

javascript
try {
  structuredClone({ f() {} });
} catch (e) {
  console.log(e.name);
}
Output

You should see one line: DataCloneError.


Circular references with structuredClone

Unlike JSON stringify, structuredClone can duplicate graphs that point back to themselves—useful when clone object js payloads include parent/child links. The clone keeps the cycle: y.a === y mirrors the original self-reference.

javascript
const x = {};
x.a = x;
const y = structuredClone(x);
console.log(y.a === y);
Output

You should see one line: true.


Summary

Match depth to risk: shallow {...obj} when shared nested state is OK; structuredClone when you need detached graphs and supported types; JSON only for JSON-safe payloads.

  • Use { ...obj } or Object.assign({}, obj) for shallow javascript clone object copies when nested sharing is acceptable.
  • Use for...in only with clear key filtering (Object.hasOwn) when you need explicit object clone javascript control.
  • Use Object.create + getOwnPropertyDescriptors when prototype and descriptors matter; still plan for nested deep copies.
  • Reserve JSON round-trip for JSON-safe javascript copy object trees; expect Date/undefined/function loss.
  • Prefer structuredClone for modern deep javascript clone work when types fit; catch DataCloneError for unsupported values.

References

MDN pages for structuredClone, spread, Object.assign, Object.create, and JSON—core APIs behind javascript clone object and javascript copy object examples in this guide.


Frequently Asked Questions

1. javascript clone object shallow vs deep?

Shallow copy duplicates top-level keys; nested objects stay the same reference. Deep copy duplicates nested data too so mutations on the clone do not affect the original nested values.

2. Is JSON.parse(JSON.stringify(obj)) a safe javascript copy object for all data?

No. It drops undefined and functions, converts Date to string, loses Map/Set semantics, and throws on circular structures. Use structuredClone when you need a native deep clone for supported types.

3. When should I use structuredClone for js clone object?

Use it for plain data graphs with Date, Map, Set, ArrayBuffer, RegExp, circular refs, etc. It throws DataCloneError for functions and some exotic types, and class instances become plain objects without the original prototype chain.

4. Does Object.assign empty object clone nested objects?

Object.assign({}, src) is shallow like spread. Nested object properties are still shared references unless you deep-clone those branches separately.

5. object clone javascript with lodash?

lodash cloneDeep is a common library choice for deep clones including edge cases; prefer structuredClone in modern runtimes when it fits your data shapes to avoid a dependency.

6. clone object javascript preserving prototype?

Use Object.create(Object.getPrototypeOf(src), Object.getOwnPropertyDescriptors(src)) for own properties and prototype linkage; values are still shallow-copied unless you add a recursive deep strategy.
Olorunfemi Akinlua

Boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs …