How to Copy a Javascript Object

Warning: This is a kinda long post. If you want a better understanding of how this stuff works, read on. If you just want to know which method to use and when, skip to the end.

If you have a Javascript object that you want to copy, Javascript doesn’t offer a straightforward way to do that which will work in every case. You might think you can just set a new variable to an existing object. This won’t work because the new variable does not get a copy of the object; it instead references it.

You’ll have two names for a single object instead of a copy. If you change the object by way of either variable, you change the object pointed to by both variables.

Although the intuitive solution won’t work, you do have a few options to do a real copy of the object.

Quick-and-Dirty Javascript Object Copy Method

For a copy of simple objects, I use this easy but hacky method. Here’s a sample object you might want to copy:

var objectToCopy = {
  name: 'Devon',
  email: 'devon@raddevon.com',
  url: 'https://raddevon.com/',
  active: true,
  phoneNumber: null
};

and here’s the method in action:

var objectCopy = JSON.parse(JSON.stringify(objectToCopy));

Now, let’s explore how and why this works. First, JSON refers to a built-in object in Javascript. It has a couple of methods you can use to deal with JSON in your Javascript code. JSON.stringify lets you convert a Javascript object to a JSON string. JSON.parse lets you go the other direction, converting a JSON string into an object.

By using both, we first make a JSON string from the object we want to copy (JSON.stringify(objectToCopy)). Then, we take that JSON string and make a new object from it (with JSON.parse()). Now, we have a new object that is effectively a copy of the old one. Changing values in one will not affect the other.

This method works pretty well and is the easiest. It falls apart, though, if your object has more complex data. JSON supports strings, numbers, true, false, null, and nested objects (so long as they themselves contain only these types). In order to copy your object using this method, it can only contain these data types.

Here’s what happens if we try to break that rule.

var objectToCopy = {
  name: 'Devon',
  email: 'devon@raddevon.com',
  url: 'https://raddevon.com/',
  active: true,
  phoneNumber: null,
  latestTweet: function() {
    // Function that returns a tweet
  }
};

var objectCopy = JSON.parse(JSON.stringify(objectToCopy));

The contents of objectCopy is this:

{
  name: 'Devon',
  email: 'devon@raddevon.com',
  url: 'https://raddevon.com/',
  active: true,
  phoneNumber: null
}

That’s because JSON.stringify fails silently (does not throw an error) for object properties it cannot serialize (convert to JSON). It tries to convert the function at property at latestTweet, but it can’t. It converts the other properties and returns a string with just those included.

When we use JSON.parse to build an object out of that string, it leaves us an object with copies of the other properties from the original but with the latestTweet property missing completely.

If you had a Javascript date in your object, you’d get a slightly different but probably still undesirable result. Let’s take this object:

var objectToCopy = {
  name: 'Devon',
  email: 'devon@raddevon.com',
  url: 'https://raddevon.com/',
  active: true,
  phoneNumber: null,
  birthdate: new Date(1983, 1, 1)
};

Copy this object with our JSON method, and you’ll get this:

{
  name: 'Devon',
  email: 'devon@raddevon.com', 
  url: 'https://raddevon.com/',  
  active: true,  
  phoneNumber: null,
  birthdate: "1983-02-01T08:00:00.000Z"
}

Check out the birthdate property. Once it’s run through JSON.stringify, the birthdate property becomes a date string instead. It’s just a string with the date in it; nothing special. When we parse it with JSON.parse, we get back an object with the birthdate as a string instead of an object. That could wreak havoc with our code since it’s probably expecting a date object instead.

Other Methods

Object.assign()

Object.assign is the best method if you need to copy only a single level of an object and if you need to copy an object with more complex properties (like the functions and date object we discussed earlier). It looks like this:

var objectCopy = Object.assign({}, objectToCopy);

This method is for merging objects together. The first argument (in our case an empty object: {}) is the source — the object to be merged into. By providing an empty object, we won’t be polluting the object we’re copying with any unwanted properties. The second and subsequent arguments are the objects to merge onto the source.

Javascript will take all the properties on the second object and copy them to the first object. It will return the new object. That’s the copy which will be assigned to objectCopy.

Unlike our quick-and-dirty method, this will copy functions and objects properly. Problem is, it won’t copy more than one level of the object. This means if one of your object’s properties is another object, it won’t be copied. Changes to nested objects on the copy will also change the original.

var objectToCopy = {
  name: {
    first: 'Devon',
    last: 'Campbell'
  },
  // 'name' will not be copied
  email: 'devon@raddevon.com',
  url: 'https://raddevon.com/',
  active: true,
  phoneNumber: null,
  birthdate: new Date(1983, 1, 1)
}

The major problem with this method is that it’s part of the ES6 spec. That means, it’s not supported everywhere. For example, the Internet Explorer browser does not support Object.assign. If you’re using older versions of Node, you’ll find that it might not work there either.

Lodash

Lodash is a library providing Javascript developers with methods for lots of common tasks in the language. It fills in a few gaps in the language. One of those is cloning objects.

Lodash offers cloneDeep which is pretty easy to use.

var objectCopy = _.cloneDeep(objectToCopy);

This is the most robust solution. It will copy an object recursively (meaning it also copies nested objects), and it preserves the original properties of the object perfectly. You won’t lose functions and other kinds of objects like dates.

Why wouldn’t you use it all the time then? In order to use Lodash, you’ll have to include the entire library in your code. If you use this in your front-end code, this is an additional request and another 4kB of data your users will have to download just to copy objects. If you’re writing back-end code, it’s not as big a deal, but you should still be careful when adding dependencies to your code.

What to Use When

  • Use Object.assign if you only need a shallow copy and if you can use ES6.
  • Use JSON.stringify and JSON.parse if you need a deep copy or you can’t use ES6. Make sure your object has only simple data like strings, numbers, true, false, null, and nested objects.
  • Use Lodash’s cloneDeep if you need a deep copy and your object has functions or complex data types like dates.

Get my web development tips in your inbox!

Enter your name and email, and I'll send you more tips like this one as soon as they go live on the site!

We won't send you spam. Unsubscribe at any time. Powered by ConvertKit

2 comments