Dmitry Fadeev
Dmitry Fadeev

Reputation: 2083

Is this a good way to clone an object in ES6?

Googling for "javascript clone object" brings some really weird results, some of them are hopelessly outdated and some are just too complex, isn't it as easy as just:

let clone = {...original};

Is there anything wrong with this?

Upvotes: 195

Views: 183731

Answers (11)

Bins
Bins

Reputation: 145

UPDATE: 05/31/2023 a new global function was release that allows DEEP COPY called window.structuredClone()

const person1 = {
    address: {
    address1: "my address1", 
    address2: "my address2"
    },
    age: 23
};
console.log(person1)
const person2 = window.structuredClone(person1)
person2.address.address1 = "my new address1"
person2.address.address2= "my new address2"
person2.age = 40
console.log(person1) // person1 was not changed

Upvotes: 8

I found a solution which seems to copy functions as well, correct me if this example is an error.

Attention I have not tested this method with more complex object cases, which, for example, would include methods with this for reference

Take for example the price of a breakfast, I have this price available globally but I would like to adjust it individually for a hotel room

// make an object for a booking option
var opt_resa = { breakfast_val: 900 }

// i define a function for opt_resa : 
opt_resa.func = function(){ alert('i am a function'); }

// copy object in modif.opt_resa :
var modif = { opt_resa : {} }

for ( var v in opt_resa ){

    modif.opt_resa[v] = opt_resa[v];
}

// test
modif.opt_resa.breakfast_val = 1500;

// old value
console.log( opt_resa.breakfast_val );
// output : 900

// modified value
console.log( modif.opt_resa.breakfast_val );
// output : 1500

// function copied
modif.opt_resa.func(); 
// this function works

Upvotes: 0

Mohamed Ageeb
Mohamed Ageeb

Reputation: 21

structured Clone you can Used this method

function Copy_Object(obj) { return structuredClone(obj); }

Upvotes: 1

Rafeeque
Rafeeque

Reputation: 933

You can do it like this as well,

let copiedData = JSON.parse(JSON.stringify(data));

Upvotes: 4

Alberto Rivera
Alberto Rivera

Reputation: 3752

EDIT: When this answer was posted, {...obj} syntax was not available in most browsers. Nowadays, you should be fine using it (unless you need to support IE 11).

Use Object.assign.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

However, this won't make a deep clone. There is no native way of deep cloning as of yet.

EDIT: As @Mike 'Pomax' Kamermans mentioned in the comments, you can deep clone simple objects (ie. no prototypes, functions or circular references) using JSON.parse(JSON.stringify(input))

Upvotes: 77

Mark Shust at M.academy
Mark Shust at M.academy

Reputation: 6429

This is good for shallow cloning. The object spread is a standard part of ECMAScript 2018.

For deep cloning you'll need a different solution.

const clone = {...original} to shallow clone

const newobj = {...original, prop: newOne} to immutably add another prop to the original and store as a new object.

Upvotes: 305

Saksham
Saksham

Reputation: 9380

All the methods above do not handle deep cloning of objects where it is nested to n levels. I did not check its performance over others but it is short and simple.

The first example below shows object cloning using Object.assign which clones just till first level.

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

newPerson = Object.assign({},person);
newPerson.skills.lang = 'angular';
console.log(newPerson.skills.lang); //logs Angular

Using the below approach deep clones object

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

anotherNewPerson = JSON.parse(JSON.stringify(person));
anotherNewPerson.skills.lang = 'angular';
console.log(person.skills.lang); //logs javascript

Upvotes: -1

Shane Gannon
Shane Gannon

Reputation: 7728

Following on from the answer by @marcel I found some functions were still missing on the cloned object. e.g.

function MyObject() {
  var methodAValue = null,
      methodBValue = null

  Object.defineProperty(this, "methodA", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    },
    enumerable: true
  });

  Object.defineProperty(this, "methodB", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    }
  });
}

where on MyObject I could clone methodA but methodB was excluded. This occurred because it is missing

enumerable: true

which meant it did not show up in

for(let key in item)

Instead I switched over to

Object.getOwnPropertyNames(item).forEach((key) => {
    ....
  });

which will include non-enumerable keys.

I also found that the prototype (proto) was not cloned. For that I ended up using

if (obj.__proto__) {
  copy.__proto__ = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
}

PS: Frustrating that I could not find a built in function to do this.

Upvotes: 2

shaheer shukur
shaheer shukur

Reputation: 1167

If the methods you used isn't working well with objects involving data types like Date, try this

Import _

import * as _ from 'lodash';

Deep clone object

myObjCopy = _.cloneDeep(myObj);

Upvotes: 8

Mohamed Elshahawy
Mohamed Elshahawy

Reputation: 113

We can do that with two way:
1- First create a new object and replicate the structure of the existing one by iterating 
 over its properties and copying them on the primitive level.

let user = {
     name: "John",
     age: 30
    };

    let clone = {}; // the new empty object

    // let's copy all user properties into it
    for (let key in user) {
      clone[key] = user[key];
    }

    // now clone is a fully independant clone
    clone.name = "Pete"; // changed the data in it

    alert( user.name ); // still John in the original object

2- Second we can use the method Object.assign for that 
    let user = { name: "John" };
    let permissions1 = { canView: true };
    let permissions2 = { canEdit: true };

    // copies all properties from permissions1 and permissions2 into user
    Object.assign(user, permissions1, permissions2);

  -Another example

    let user = {
      name: "John",
      age: 30
    };

    let clone = Object.assign({}, user);
It copies all properties of user into the empty object and returns it. Actually, the same as the loop, but shorter.

But Object.assign() not create a deep clone

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

To fix that, we should use the cloning loop that examines each value of user[key] and, if it’s an object, then replicate its structure as well. That is called a “deep cloning”.

There’s a standard algorithm for deep cloning that handles the case above and more complex cases, called the Structured cloning algorithm. In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library lodash the method is called _.cloneDeep(obj).

Upvotes: 0

marcel
marcel

Reputation: 3272

if you don't want to use json.parse(json.stringify(object)) you could create recursively key-value copies:

function copy(item){
  let result = null;
  if(!item) return result;
  if(Array.isArray(item)){
    result = [];
    item.forEach(element=>{
      result.push(copy(element));
    });
  }
  else if(item instanceof Object && !(item instanceof Function)){ 
    result = {};
    for(let key in item){
      if(key){
        result[key] = copy(item[key]);
      }
    }
  }
  return result || item;
}

But the best way is to create a class that can return a clone of it self

class MyClass{
    data = null;
    constructor(values){ this.data = values }
    toString(){ console.log("MyClass: "+this.data.toString(;) }
    remove(id){ this.data = data.filter(d=>d.id!==id) }
    clone(){ return new MyClass(this.data) }
}

Upvotes: 3

Related Questions