Introduction: Why Simple Assignment Is a Trap?
\nImagine you are writing a notes application. A user creates a card with a title and text, then wants to make a copy of it to modify it slightly. You write:
\nconst original = { title: 'My Plan', text: 'Buy milk' };\nconst copy = original;\ncopy.title = 'New Plan';\nconsole.log(original.title); // 'New Plan' — oops!\nSound familiar? If so, you've encountered the main feature of objects in JavaScript: they are passed by reference. The assignment copy = original does not create a new object, but merely copies the reference to the same memory location. Any change in copy is reflected in original.
On Stack Overflow, the question "How do I correctly clone a JavaScript object?" has gathered thousands of views and dozens of answers. This is one of the most common problems for beginners and even experienced developers. In this article, we will break down all the ways to clone objects in JavaScript, from simple to advanced, and find out which approach is best for each situation.
\n\nShallow Copy: Fast and Simple
\nShallow copy creates a new object, but for nested objects (arrays, other objects) it only copies references. This is ideal for flat structures, but dangerous for deep ones.
\n\nMethod 1: Spread Operator (...)
\nThe most modern and readable way:
\nconst original = { name: 'Anna', age: 25 };\nconst clone = { ...original };\nclone.age = 30;\nconsole.log(original.age); // 25 — all good!\nPros: short syntax, works with arrays and objects. Cons: does not copy methods (functions) and symbol properties.
\n\nMethod 2: Object.assign()
\nThe classic method for copying an object without references at the top level:
\nconst source = { a: 1, b: { c: 2 } };\nconst target = Object.assign({}, source);\ntarget.a = 100; // source.a will not change\ntarget.b.c = 999; // source.b.c will also change — shallow!\nThis method is especially useful when you need to merge multiple objects: Object.assign({}, obj1, obj2).
Method 3: for...in Loop
\nAn ancient but still working method. Useful for understanding the mechanics:
\nfunction shallowClone(obj) {\n const clone = {};\n for (let key in obj) {\n if (obj.hasOwnProperty(key)) {\n clone[key] = obj[key];\n }\n }\n return clone;\n}\nToday it is rarely used, but it perfectly demonstrates how manual object cloning works.
\n\nDeep Copy: When You Need Everything
\nIf your object contains nested objects, arrays, or dates, shallow copy will let you down. You need deep cloning js.
\n\nMethod 4: JSON.parse + JSON.stringify
\nThe most popular "workaround":
\nconst original = { \n name: 'Peter', \n address: { city: 'Moscow', street: 'Tverskaya' },\n date: new Date()\n};\nconst clone = JSON.parse(JSON.stringify(original));\nclone.address.city = 'SPb';\nconsole.log(original.address.city); // 'Moscow' — hooray!\nBut there are nuances: this method loses functions, undefined, Symbol, and Date turns into a string. Fine for simple data, not for complex ones.
Method 5: structuredClone() — The New Standard
\nSince 2022, JavaScript has a built-in method structuredClone:
\nconst original = { \n scores: [10, 20, 30], \n meta: { level: 'easy' },\n date: new Date()\n};\nconst clone = structuredClone(original);\nclone.meta.level = 'hard';\nconsole.log(original.meta.level); // 'easy'\nPros: supports Date, Map, Set, ArrayBuffer, recursive references. Cons: does not support functions, DOM elements, Error. Works