- Published on
Deep copy vs Shallow copy
- Authors
- Name
- Khánh
Introduction
Copying is a common practice in programming, often understood as "cloning" the code. For immutable values, copying is straightforward, but for objects and arrays, it becomes more complex. JavaScript introduces two concepts for copying: shallow copy and deep copy.
Shallow Copy
A shallow copy of an object involves copying its properties and sharing references between them - meaning they point to the same value.
After copying, if you change the value of one variable, the value of the other variable will also change.
More formally, two objects o1
and o2
are shallow copies if:
- They are not the same object
(o1 !== o2)
. - The properties of
o1
ando2
have the same names in the same order. - The values of their properties are equal.
- Their prototype chains are equal.
Deep copy
Deep copy involves copying all properties without sharing references (not referencing the values in memory).
After copying, if you change the value of one variable, it will not affect the other variable.
We can define deep copies more formally as:
- They are not the same object
(o1 !== o2)
. - The properties of
o1
ando2
have the same names in the same order. - The values of their properties are deep copies of each other.
- Their prototype chains are structurally equivalent.
Spread operator - shallow copy
The spread operator ...
in JavaScript is used to copy the data of an object - it's important to note that this performs shallow copy only.
let x = {
a: {text: "khanh"},
b: "hello",
}
let y = {...x};
y.a.text = "no name";
y.b = "xin chao";
console.log(x)
// {
// "a": {
// "text": "no name"
// },
// "b": "hello"
// }
Shallow copy only copies primitive values, so the value of y.b
will be different from x.b. Therefore, when you change the value of y.b
, the value of x.b
does not change.
However, x.a.text
is changed because the spread operator ...
performs a shallow copy, meaning y
references the memory location of x
(which is an object here), so the value is changed accordingly.
Object.assign - shallow copy
Object.assign
is a method used to copy all enumerable own properties from one or more source objects to a target object. It returns the modified target object.
const target = { a: 1, b: 2, };
const source = { b: 4, c: 5, d: {text: "hi"} };
const returnedTarget = Object.assign(target, source);
source.d.text = "modified";
console.log(source)
// Object { b: 4, c: 5, d: Object { text: "modified" } }
console.log(target)
// Object { a: 1, b: 4, c: 5, d: Object { text: "modified" } }
console.log(returnedTarget === target);
// true
JSON.parse(JSON.stringify(…)) - deep copy
Deep copy can work with simple objects and can be serialized - which is the process of converting an object or data structure into a suitable format for transmission over a network or storage.
However, functions (including those with closures), objects representing HTML elements, recursive data, and many other cases will fail if you attempt to deep copy them using JSON.stringify()
. There is no straightforward way to deep copy these types of data.
const ingredientsList = ["noodles", { list: ["eggs", "flour", "water"] }];
const ingredientsListDeepCopy = JSON.parse(JSON.stringify(ingredientsList));
structuredClone - deep copy
// Example object with circular reference
const obj = {
name: 'John',
age: 30,
address: {
city: 'New York',
country: 'USA'
}
};
obj.self = obj; // Circular reference
// Using structuredClone for deep copy
const clonedObj = structuredClone(obj);
// Output the cloned object
console.log(clonedObj);
// structuredClone function definition (browser environment)
function structuredClone(obj) {
return new Promise((resolve, reject) => {
const { port1, port2 } = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}