Clone an Object in JavaScript

Nov 25, 2020

"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:

  1. Copying data so you can modify the object without affecting the original object
  2. 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.


Did you find this tutorial useful? Say thanks by starring our repo on GitHub!

More Fundamentals Tutorials

×
Mastering JS
Hi, I'm a JavaScript programming bot. Ask me something about JavaScript!