Clone an Object in JavaScript
"Cloning" an object in JavaScript means creating a new object with the same properties as the original object. Objects in JavaScript are stored by reference, which means that two variables can point to the same object in memory. Modifying one object variable can impact other variables.
const obj1 = { a: true, b: true };
const obj2 = obj1;
obj2.c = true;
obj1.c; // true, because `obj1` points to the same object as `obj2`
The two most common reasons to clone objects in JavaScript are:
- Copying data so you can modify the object without affecting the original object
- Working with frameworks that rely on immutability for diffing, like React
Whether you're cloning for one of these reasons, or a different reason entirely, is important for determining what pattern you should use for cloning. Here are 3 different approaches:
Shallow Clone using Spread Operator or Object.assign()
The easiest ways to shallow clone an object in vanilla JavaScript are using the spread operator or the Object.assign()
function. These approaches are functionally similar, but the spread operator is slightly faster.
const obj1 = { a: true, b: true };
// Copy `obj1` using the spread operator:
const obj2 = { ...obj1 };
// Copy `obj1` using the `Object.assign()` function:
const obj3 = Object.assign({}, obj1);
obj2.c = true;
obj3.d = true;
Object.keys(obj1); // ['a', 'b']
The spread operator is commonly used for immutable updates for React projects. The idea is that every time you update an object, you clone
the object. Cloning the object every time you update it makes checking for changes easier, because
you can use ===
to check whether the object changed.
const oldState = { count: 0, username: 'test' };
// Instead of `++oldState.count`, you can clone and create a new object
const newState = { ...oldState, count: 1 };
// Checking if something changed is much easier!
oldState === newState; // false
While pattern of copying objects to modify them is common, do not use this approach unless you're using React and you're sure you need to. In most cases, cloning a whole object to modify one property is highly wasteful, and this pattern can also lead to bugs in other frameworks.
Deep Clone using JSON.stringify()
Shallow cloning using the spread operator is easy and relatively fast. But, because it is a shallow clone rather than a deep clone, it doesn't recursively clone nested objects!
const obj1 = {
a: { value: true },
b: { value: true }
};
// Copy `obj1` using the spread operator:
const obj2 = { ...obj1 };
obj2.a.value = false;
// false, because `a` points to the same object after shallow cloning!
obj1.a.value;
In other words, "deep clone" just means "recursively shallow clone all objects." One trick you
can use to deep clone an object without writing any recursion yourself is to use JSON.parse(JSON.stringify(obj1))
. In other words, convert the object you want to clone into JSON, and then
parse it again.
const obj1 = {
a: { value: true },
b: { value: true }
};
// Deep clone `obj1` using parse and stringify.
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a.value = false;
// true, because `obj2` is a deep clone
obj1.a.value;
While JSON.parse(JSON.stringify())
is easy, it comes with a lot of caveats. Using this pattern
works fine if your object only contains primitive values, POJOs, and arrays. But once you introduce
classes like Date
, this pattern fails to actually clone the object, because JSON.stringify()
converts dates to strings.
const obj = { date: new Date('2019-06-01') };
const copy = JSON.parse(JSON.stringify(obj));
obj.date instanceof Date; // true
copy.date instanceof Date; // false, `date` is a string
Deep Clone Using Lodash
Lodash's deepClone()
function is a much more robust
deep clone than JSON.parse(JSON.stringify())
. It handles many common edge cases, like dates and
Node.js buffers. For example:
const obj1 = {
date: new Date('2019-06-01'),
buffer: Buffer.from('hello, world'),
a: { value: true }
};
const obj2 = _.cloneDeep(obj1);
obj1.date === obj2.date; // false
obj1.date.toString() === obj2.date.toString(); // true
obj1.buffer === obj2.buffer; // false
obj1.buffer.toString('utf8') === obj2.buffer.toString('utf8'); // true
obj1.a === obj2.a; // false
If you're looking to copy an arbitrary object that may contain nested objects so you can safely
modify any property without affecting the original object, _.cloneDeep()
is the way to go.
Recursively using the spread operator is tricky if you don't know the structure of the object,
although you can use the spread operator if you know for a fact the objects you're cloning don't
have nested objects.