Due to the fact that objects are reference values in JavaScript, copying them is not as easy as just assigning to another variable.
There are a lot of different ways to copy an object.
Choosing the right method depends on what would you like to achieve.
Before we start with listing the most popular methods and providing use cases for each and every one of them, let's quickly remind ourselves what is shallow and deep copy and the differences between them.
Shallow copy is a bit-wise copy of an object. A new object is created that has an exact copy of the values in the original object. If any of the fields of the object are references to other objects, just the reference addresses are copied i.e., only the memory address is copied.
Consider the following example:
const user = {
name: "John",
surname: "Doe",
other: {
age: 18,
},
};
const newUser = {
...user,
};
newUser.other.age = 22;
// Prints {name: "John", surname: "Doe", age: 22}
console.log(user);
// Prints {name: "John", surname: "Doe", age: 22}
console.log(newUser);
Property other
references to an object which contains age
.
When doing a shallow copy, just the reference address of other
is copied, not the value itself.
That's why when we modify other.age
it gets updated in both user
and newUser
.
Making a deep copy of an object means copying everything, the newly copied object is completely independent of the original one:
import cloneDeep from "lodash/cloneDeep";
const user = {
name: "John",
surname: "Doe",
other: {
age: 18,
},
};
const newUser = cloneDeep(user);
newUser.other.age = 22;
// Prints {name: "John", surname: "Doe", age: 18}
console.log(user);
// Prints {name: "John", surname: "Doe", age: 22}
console.log(newUser);
Having in mind the differences between shallow and deep copy, let's start with listing the most popular ways of copying the object:
does a shallow copy
const user = {
name: "John",
surname: "Doe",
age: 18,
};
// newUser is a shallow copy of user
const newUser = {
...user,
};
does a shallow copy
Object.assign(target, ...sources)
accepts 2 parameters:
target
- a target object, where to copysources
- source objects, what to copyCopy one source object into an empty target:
const user = {
name: "John",
surname: "Doe",
age: 18,
};
// newUser is a shallow copy of user
const newUser = Object.assign({}, user);
Copy multiple source objects into an empty target:
const user = {
name: "John",
surname: "Doe",
age: 18,
};
const address = {
street: "Street",
house: 4,
flat: 1,
};
// newUser is a shallow copy of user
const newUser = Object.assign({}, user, address);
Copy one source object to an existing target:
const target = {
name: "John",
surname: "Doe",
age: 18,
};
const address = {
street: "Street",
house: 4,
flat: 1,
};
// newUser is a shallow copy of user
const newUser = Object.assign(target, address);
Important note: make sure to remember that target
object is always mutated.
does a deep copy
Note, that this is not the optimal way of cloning an object, consider using cloneDeep
from lodash:
const user = {
name: "John",
surname: "Doe",
age: 18,
};
// newUser is a deep copy of user
const newUser = JSON.parse(JSON.stringify(user));
You can ask, why is it bad practice? Let me try to explain it to you.
JSON.stringify
converts value to JSON string.
While it works good with primitives, it has some troubles with:
undefined
values:const obj = {
key: undefined,
};
console.log(JSON.stringify(obj)); // Prints "{}"
const obj = {
key: Symbol(),
};
console.log(JSON.stringify(obj)); // Prints "{}"
const obj = {
key: new Date(),
};
// Prints Date object: { key: 2020-05-25T14:28:42.155Z }
console.log(obj);
// Prints string: {"key":"2020-05-25T14:27:27.413Z"}
console.log(JSON.parse(JSON.stringify(obj)));
const user = {
name: "John",
surname: "Doe",
age: 18,
};
user.brother = user;
// TypeError: Converting circular structure to JSON
console.log(JSON.parse(JSON.stringify(user)));
const obj = {
key: () => {},
};
console.log(JSON.stringify(obj)); // Prints "{}"
You can potentially lose some data without even knowing that, as you wouldn't even be warned by the JavaScript.
Calling JSON.stringify
with such data types doesn't throw any errors.
To sum it up, try to avoid this way of cloning an object.
The best way to create a deep copy of an object - is to use a popular, well tested external library, like lodash.
Lodash provides us with cloneDeep
method:
import cloneDeep from "lodash/cloneDeep";
const user = {
name: "John",
surname: "Doe",
age: 18,
};
// newUser is a deep copy of user
const newUser = cloneDeep(user);
Lodash also implements clone
method, which does a shallow copy of an object:
import clone from "lodash/clone";
const user = {
name: "John",
surname: "Doe",
age: 18,
};
// newUser is a shallow copy of user
const newUser = clone(user);
There's always a possibility to create your own custom method that would clone an object, but that's like reinventing the wheel.
You will spend a lot of time writing your own implementation, fixing bugs, and covering the code with unit tests so there's no need to do that.
Always remember that if you encounter any kind of problem in programming, you are for sure not the first person to face that, so give yourself a try to search for a ready-to-use solution on the internet.
To create a shallow copy of an object use:
To create a deep clone on an object use:
Important note: Never use the assignment operator =
for cloning objects.