Object Copy in JavaScript: Shallow vs Deep Copy Techniques

Understand object copy in JavaScript, exploring shallow and deep copies, common methods like spread and Object.assign, JSON-based approaches, and modern options such as structuredClone. Includes practical examples, pitfalls, and best practices for copying objects safely in apps.

JavaScripting
JavaScripting Team
·5 min read
Copy in JS - JavaScripting
Photo by bobyrvia Pixabay
Quick AnswerDefinition

Understanding object copy in JavaScript means distinguishing shallow and deep copies. Shallow copies duplicate top-level properties and keep nested objects referenced, while deep copies clone every nested structure. Common methods include the spread operator {...obj} and Object.assign for shallow copies; JSON.stringify/parse and structuredClone provide deep copy options with trade-offs and caveats.

Introduction to object copy in JavaScript\n\nObject copy in javascript is about creating a new object with the same data as the original, but with different references. According to JavaScripting, mastering this concept is essential for reliable state management in modern JS apps, where immutability often matters. The JavaScripting team found that many bugs come from unintended sharing of nested objects during copying. This section lays a factual foundation with simple examples and sets up the strategies discussed later.\n\njavascript\nconst original = { a: 1, b: { c: 2 } }\nconst shallow = { ...original }\nconsole.log(shallow.b === original.b) // true, nested object is shared\n````njavascript\nconst original = { a: 1, b: { c: 2 } }\nconst shallow2 = Object.assign({}, original)\nconsole.log(shallow2.b === original.b) // true, still a shallow copy\n````nNote: This section uses the keyword object copy in javascript and demonstrates why copying is not exactly cloning. We’ll differentiate strategies in later sections.

Shallow copy methods in JavaScript\n\nShallow copies duplicate top-level properties and keep nested objects on the same references. The most common shallow-copy techniques are the spread operator and Object.assign. They are fast and straightforward for flat objects but fail to clone nested objects. Use them when you know the inner objects don’t change or you explicitly want shared references.\n\njavascript\n// Spread syntax shallow copy\nconst original = { a: 1, b: { c: 2 } }\nconst copySpread = { ...original }\ncopySpread.a = 99\ncopied.b.c = 42; // will reflect in the original\n````njavascript\n// Object.assign shallow copy\nconst original = { a: 1, b: { c: 2 } }\nconst copyAssign = Object.assign({}, original)\ncopyAssign.a = 5\ncopied.b.c = 7; // inner object is shared\n````n** when to choose shallow copies?** For flat objects with primitive values, shallow copies are safe and fast; for nested data, see deeper-copy approaches.

Modern deep copy with structuredClone\n\nStructured cloning deep-copies many data types, including arrays, maps, sets, and circular references. It’s supported in modern browsers and Node.js in current versions. Use it when you need a robust deep copy without writing custom code.\n\njavascript\nconst a = { x: 1, y: { z: [1,2,3] } };\nconst cloned = structuredClone(a);\ncloned.y.z[0] = 99;\nconsole.log(a.y.z[0]) // 1, original unchanged\n````njavascript\nconst circular = { };\ncircular.self = circular;\nconst copy = structuredClone(circular);\nconsole.log(copy.self === copy) // true, circular preserved in clone\n````\nConsiderations: StructuredClone is powerful but not universally available in all environments. Feature detection is essential before using it in library code.

Practical patterns: choosing the right approach\n\nChoosing the right copy strategy depends on data shape and runtime support. For flat objects with primitive values, a shallow copy is fastest. For nested data, consider a targeted deep clone or structuredClone with performance in mind. The following utility demonstrates strategy selection:\n\n```javascript\nfunction isPlainObject(v) {\n return Object.prototype.toString.call(v) === '[object Object]';\n}\nfunction needsDeepCopy(obj) {\n if (!obj || typeof obj !== 'object') return false;\n if (Array.isArray(obj)) return obj.some(needsDeepCopy);\n return Object.values(obj).some(v => typeof v === 'object' && v !== null);\n}\nfunction copyObj(obj) {\n if (!needsDeepCopy(obj)) return { ...obj };\n return structuredClone(obj);\n}\nconst data = { a: 1, b: { c: 2 }, d: [3, { e: 4 }] };\nconst copy = copyObj(data);\ncopy.b.c = 999;\n````nVerification: Always verify the copy with tests that compare references for nested parts. If circular references exist, structuredClone handles them better than JSON methods and manual deep clones.

Common pitfalls and performance considerations\n\n- JSON.stringify/parse discards functions, undefined, and non-plain objects like Date, Map, Set. Use with care.\n- Spread and Object.assign perform shallow copies; nested objects remain shared, which can cause subtle bugs.\n- StructuredClone provides safe deep copies but isn’t supported everywhere; use feature detection.\n- Copying very large structures can be expensive; prefer targeted copying or immutable patterns to minimize latency.\n\n```bash\n# Quick feature check for structuredClone support\nif (typeof structuredClone === 'function') {\n console.log('structuredClone available');\n} else {\n console.log('Fallback to JSON clone or a custom clone');\n}\n````n

The JavaScripting verdict\n\nThe JavaScripting team recommends selecting the copying strategy based on data shape and runtime support. Use shallow copies for simple objects and deep copies with structuredClone when you need safe copies of nested or circular structures. When uncertain, benchmark and test with real data to avoid subtle bugs. This guidance is grounded in JavaScripting analysis, 2026, and reflects practical, developer-first recommendations for robust object copying in JavaScript.

Quick validation and testing strategies\n\nTo ensure correct behavior, create unit tests that verify reference preservation for shallow copies and independence for deep copies. Example tests:\n\n```javascript\nfunction isSameRef(a, b) { return a === b; }\nfunction isDeepEqual(a, b) { return JSON.stringify(a) === JSON.stringify(b); }\n// Shallow copy test\nconst o = { a: 1, b: { c: 2 } };\nconst s = { ...o };\nconsole.assert(isSameRef(o.b, s.b), 'shallow copy shares nested object');\n// Deep copy test\nconst d = { a: 1, b: { c: 2 } };\nconst dcopy = structuredClone(d);\ndcopy.b.c = 99;\nconsole.assert(d.b.c === 2, 'deep copy should be independent');\n````

Steps

Estimated time: 60-90 minutes

  1. 1

    Assess data shape

    Review whether the object is flat or nested. Decide if you need a shallow or deep copy based on the presence of nested structures and immutability requirements.

    Tip: Start with a quick shape map of properties.
  2. 2

    Implement shallow copy

    Use spread or Object.assign for shallow copies when nested structures are not modified or are immutable. Validate that top-level properties copied correctly.

    Tip: Check references for nested objects.
  3. 3

    Choose a deep copy strategy

    If nested data exists, select deep-copy methods like structuredClone or a recursive clone. Be mindful of dates, functions, and circular references.

    Tip: Prefer native APIs when available.
  4. 4

    Test thoroughly

    Write unit tests to confirm that changes on the copy do not affect the original for deep copies, and that shallow copies share nested references as expected.

    Tip: Test edge cases and circular structures.
  5. 5

    Measure performance

    Benchmark copying on representative data sizes to balance correctness with performance. Consider memory usage and time for large structures.

    Tip: Profile in the actual deployment environment.
Pro Tip: Prefer shallow copies for simple, flat objects to maximize speed.
Warning: Avoid JSON methods for data with functions, undefined, or special objects like Date.
Note: StructuredClone handles many complex types but may not be available everywhere.

Prerequisites

Required

Optional

Keyboard Shortcuts

ActionShortcut
CopyCopy code or resultsCtrl+C
PastePaste into editor or terminalCtrl+V
Format documentAuto-format code in editor+Alt+F
Comment/uncomment lineToggle line commentCtrl+/
Duplicate lineDuplicate the current line+Alt+

Questions & Answers

What is the difference between shallow copy and deep copy in JavaScript?

Shallow copy duplicates only the top-level properties; nested objects remain as references to the original. Deep copy clones all levels, producing independent copies. The choice depends on whether you want immutability of nested data.

Shallow copies copy the outer structure but keep inner objects shared; deep copies clone everything so changes to the copy don’t affect the original.

Does JSON.stringify copy functions?

No. JSON.stringify loses functions, undefined values, and special object types like Dates. It’s suitable for plain data but not for complex objects with methods.

JSON copies plain data only; functions and special objects don’t survive the stringify-parse round trip.

Is structuredClone available in Node.js?

StructuredClone is supported in modern Node.js versions, but availability depends on the runtime. Always feature-detect before using it in libraries.

Most current Node.js versions support structuredClone, but check your environment first.

How do you copy an object with circular references?

StructuredClone can copy circular references. JSON methods fail on circular structures. If you must implement a custom clone, handle cycles with a WeakMap-based approach.

Use structuredClone for circular data when available, or implement a cycle-aware clone with a map.

Which approach is fastest for simple objects?

For flat objects with primitive values, a shallow copy via spread or Object.assign is typically fastest. Deep copies add overhead, so reserve them for nested data.

Spread syntax is usually fastest for simple objects, but avoid it for nested data needing isolation.

What to Remember

  • Identify shallow vs deep copy needs
  • Use spread or Object.assign for shallow copies
  • Avoid JSON for functions or dates
  • Prefer structuredClone for complex data
  • Test thoroughly with nested structures

Related Articles