All the ways you can clone an object in JavaScript

Written by David Abram

In JavaScript, object cloning isn't as obvious as it seems. You can't just assign the value of one object to another variable and create a copy of it. The problem is related to the data types you can use in JavaScript.

There are two groups of JavaScript data types; primitive values and objects. Primitive values are Boolean, Null, Undefined, Number, BigInt, String & Symbol. Objects are all other data types that are collections of properties (including Arrays!).

The big difference between primitive values and objects is shown when using the = operator.


Primitives

When working with primitives, the = operator creates a copy of the original variable. We call this 'passing by value'.

var smiles = 'πŸ˜€πŸ˜πŸ˜†';

// 'πŸ˜€πŸ˜πŸ˜†'
console.log(smiles);

var animals = smiles;

// 'πŸ˜€πŸ˜πŸ˜†'
console.log(animals);



animals = '🐺🦊🐻';

// '🐺🦊🐻'
console.log(animals);

// 'πŸ˜€πŸ˜πŸ˜†'
console.log(smiles);

Variable 'smiles' is a primitive value which means that the variable 'animals' in the line var animals = smiles; becomes a copy of the variable 'smiles'.

When you change the value of the variable 'animals' in the line animals = '🐺🦊🐻';, it doesn't change the value of the variable 'smiles'.

Objects

When working with objects, the = operator creates an 'alias' to the original object, it doesn’t create a new object. We call this 'passing by reference'.

var drinks = {
  cocktail: '🍹',
  coffee: 'β˜•',
  beer: '🍺',
} 

// { cocktail: '🍹', coffee: 'β˜•', beer: '🍺' } 
console.log(drinks);


var foods = drinks;

// { cocktail: '🍹', coffee: 'β˜•', beer: '🍺' } 
console.log(foods);

foods.cocktail = '🍀';
foods.coffee = '🧁';
foods.beer = 'πŸ–';

// { cocktail: '🍀', coffee: '🧁', beer: 'πŸ–' } 
console.log(foods);

// { cocktail: '🍀', coffee: '🧁', beer: 'πŸ–' } 
console.log(drinks);

The type of variable 'drinks' is an object, which means variable 'foods' in the line var foods = drinks; becomes a reference to the value of the variable 'drinks'.

When you change the value of the properties of variable 'foods' in the lines foods.cocktail = '🍀'; foods.coffee = '🧁'; foods.beer = 'πŸ–';, it changes the value of properties of variable 'drinks'.

'Passing by reference' complicates the cloning process of the objects, you cannot use a simple variable assignment to copy a non-primitive value.

Hopefully, the next examples will help you learn some of the techniques for cloning objects.


Helpful links


Contents



Shallow Clone

'Shallow cloning' is an act of object cloning where you copy all the properties of an object to a newly created object, but the non-primitive properties are copied by reference.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  const clonedObject = shallowCopy(object);

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);

  object.laptop = '🍹';
  object.animals.bear = '🐻';
  object.smiles = 42;

  // { laptop: '🍹', smiles: 42,  animals: { wolf: '🐺', fox: '🦊', bear: '🐻' }, alien: Symbol('πŸ‘½')}
  console.log(object);

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊', bear: '🐻' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);

If you change the value of the properties of the non-primitive properties of the original object, you will change the value in the clonedObject.

As you can see in the line object.animals.bear = '🐻'; we are changing the property of the non-primitive property 'animals' in the original object. It changes the clonedObject.animals.bear because it was passed by reference.



Using Spread syntax

The Spread syntax (...) is a proposal that was accepted in ECMAScript 2018. It destructures the object and keys while copying their values onto a new object.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  const clonedObject = {...object};

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);
Helpful links


Using Object.assign()

By using this method you will clone all of the property values from one or more source objects to a target object.

It's also used for merging multiple objects into a single object with combined properties.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  const clonedObject = Object.assign({}, object);

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);
Helpful links


Using Object.entries & Object.fromEntries

Object.entries() transforms the object to an array of arrays containing the key names and the values of the original object properties.

Sounds confusing?

In our example, object Object.entries(object) result looks something like this: [['laptop', 'πŸ’»'], ['smiles', ['πŸ˜€', '😁', 'πŸ˜†']], ...].

Object.fromEntries() takes the array of arrays and transforms it back to an object.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  const clonedObject = Object.fromEntries(Object.entries(object));

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);
Helpful links


Deep Clone

Deep cloning is an act of object cloning where you copy all the properties of an object to a newly created object and to all of the properties of all non-primitive properties of that object, and so on recursively.

You will get a true clone of an object you want to clone; it's doesn't reference any value of the original object.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  const clonedObject = shallowCopy(object);

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);

  object.laptop = 'πŸ–¨οΈ';
  object.animals.bear = '🐻';
  object.smiles = 42;

  // { laptop: 'πŸ–¨οΈ', smiles: 42,  animals: { wolf: '🐺', fox: '🦊', bear: '🐻' }, alien: Symbol('πŸ‘½')}
  console.log(object);

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);


Using JSON.stringify & JSON.parse

JSON.stringify() & JSON.parse() way of deep cloning offers an easy but unreliable deep cloning because it tends to lose some data along the way (Valid JSON data types don't include functions, symbols and undefined). Also, it is extremely slow for larger objects.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  const clonedObject = JSON.parse(JSON.stringify(object));

  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' } }
  console.log(clonedObject);

As you can see from the example, 'Symbol' isn't a valid JSON data type; 'alien' property is ignored in the JSON.stringify() and is missing in the clonedObject.

Helpful links


[Node.js ONLY] Using v8.serialize & v8.deserialize

v8.serialize() & v8.deserialize() is only available in Node.js environments. Unfortunately, it returns an error when trying to serialize objects that have symbols or functions as their properties.

  const v8 = require('v8');

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  const clonedObject = v8.deserialize(v8.serialize(object));

  // Uncaught Error: Symbol(πŸ‘½) could not be cloned.
  console.log(clonedObject);

The example returns an error with a Uncaught Error: Symbol(πŸ‘½) could not be cloned. message. If you remove the 'alien' property from the object, the code works.

Helpful links


Using external libraries

The following methods are just some examples of cloning using popular external libraries and it's in no way an exhaustive list.



Using $.extend()

This is a shallow and deep cloning method from a very popular jQuery library. From a usability standpoint it functions almost the same as the Object.assign(). If you pass true as the first argument, the $.extend() method will deep clone.

It works nicely in legacy projects that don't support Object.assign() or Spread Syntax (older than Chrome 45, Firefox 34 or Node.js 4.0.0).

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };

  // deep cloning -> value of the first argument is true
  const clonedObject = $.extend(true, {}, object);
  
  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);
Helpful links


Using _.clone()

Underscore clone() is also a shallow cloning method, and it's a method from a somewhat popular library but it doesn't support deep cloning.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };
  
  const clonedObject = _.clone(object);
  
  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);
Helpful links


Using _.cloneDeep()

The last method we'll cover is a deep clone method, _.cloneDeep(), which recursively copies properties from an object and keeps the object's inheritance.

Lodash is a well-tested and popular library and what's great about it is that you can import each function individually.

  const object = {
    laptop: 'πŸ’»',
    smiles: ['πŸ˜€', '😁', 'πŸ˜†'],
    animals: {
      wolf: '🐺',
      fox: '🦊',
    },
    alien: Symbol('πŸ‘½'),
  };
  
  const clonedObject = _.cloneDeep(object);
  
  // { laptop: 'πŸ’»', smiles: ['πŸ˜€', '😁', 'πŸ˜†'],  animals: { wolf: '🐺', fox: '🦊' }, alien: Symbol('πŸ‘½')}
  console.log(clonedObject);
Helpful links
David Abram
Spends his time untangling software architectures and doing DevOps. Likes to build stuff. Connect with him on Twitter and LinkedIn