Why assigning a new variable is not enough

In JavaScript, arrays and objects are reference values. Assigning one to another variable gives both variables access to the same value; it does not create a separate array or object. You can try the examples in a modern browser’s developer console. No library or build tool is needed.

For example, let original = ["red", "blue"]; let alias = original; alias.push("green"); changes original too. After those statements, original contains three colors. The same rule applies to objects: let settings = { theme: "light" }; let alias = settings; alias.theme = "dark"; leaves settings.theme equal to "dark". Use a copy when you want to change one version while keeping the other unchanged.

Make a shallow copy with spread syntax

For an array, place three dots before it inside a new array: let colors = ["red", "blue"]; let copy = [...colors]; copy.push("green");. Now colors still has two entries, while copy has three. For an object, use the same idea inside braces: let settings = { theme: "light", size: "medium" }; let copy = { ...settings }; copy.theme = "dark";. The original settings.theme remains "light". Array spread is available in JavaScript environments that support ES2015; object spread requires ES2018 support.

Spread also helps when you want a changed version without a separate assignment. For example, let updated = { ...settings, theme: "dark" }; creates a new outer object and replaces the theme value in that new object. Similarly, let extended = [...colors, "green"]; creates a new array with an added entry. The order matters for objects: properties written after ...settings take precedence over properties with the same names in settings.

Other shallow-copy options include colors.slice() for arrays and Object.assign({}, settings) for objects. None of these methods protects you from changes to every value inside the copy. They duplicate the outer array or object, which is often enough for a flat list of strings or an object whose properties are numbers, strings, booleans, or other primitive values.

Watch for nested arrays and objects

A shallow copy still shares nested objects with the original. Try let user = { name: "Sam", preferences: { theme: "light" } }; let copy = { ...user }; copy.preferences.theme = "dark";. The value of user.preferences.theme is now "dark" too. The outer objects are separate, but both preferences properties point to the same nested object.

To change a known nested value without changing the original, copy each level you plan to edit: let updated = { ...user, preferences: { ...user.preferences, theme: "dark" } };. Now updated.preferences is a separate object, and user.preferences.theme remains "light". The same principle applies to an array of objects. If let tasks = [{ title: "Read", done: false }];, then let updated = tasks.map((task, index) => index === 0 ? { ...task, done: true } : task); makes a new array and a new object for the changed task. It reuses unchanged task objects, so you should copy those too before editing them later.

This selective approach is useful when you know exactly which part of a structure needs to change. Before editing a copied value, ask two questions: Did I copy the outer container? Is the particular nested object or array I am about to edit also a copy?

Use a deep copy when you need an independent structure

If you need to copy many nested values at once, structuredClone can be convenient in a JavaScript environment that supports it. For example, let original = { profile: { name: "Sam" }, tags: ["new"] }; let copy = structuredClone(original); copy.profile.name = "Lee"; copy.tags.push("active");. Those changes do not alter original. Check that structuredClone is available in your browser or runtime before relying on it; older environments may not provide it.

A deep copy is not always the right choice. structuredClone cannot clone every kind of value, including functions and DOM elements, and it does not preserve every behavior of custom objects. For a small, known structure, copying only the levels you intend to change is often clearer. Avoid treating JSON.stringify followed by JSON.parse as a general-purpose deep-copy method: it can discard or alter values, and it cannot handle circular references.

The practical rule is to match the copy to the edit. Use spread or another shallow-copy method for flat data. Copy the relevant nested levels when changing a specific nested value. Consider structuredClone for supported data when the whole nested structure must be independent. After choosing a method, test it by changing the copy and checking the original—not just by checking whether the two outer variables are different.

Back to category ↗