Shivangi
Shivangi

Reputation: 3062

Does Object.assign() create a deep copy or a shallow copy?

I just came across this concept of

var copy = Object.assign({}, originalObject);

which creates a copy of original object into the "copy" object. However, my question is, does this way of cloning object create a deep copy or a shallow copy?

PS: The confusion is, if it creates a deep copy, then it would be the easiest way to clone an object.

Upvotes: 71

Views: 67862

Answers (7)

Marian07
Marian07

Reputation: 2582

For small Data structures I see that JSON.stringify() and JSON.parse() work nice.

// store as JSON
var copyOfWindowLocation = JSON.stringify(window.location)
console.log("JSON structure - copy:", copyOfWindowLocation)
// convert back to Javascript Object
copyOfWindowLocation = JSON.parse(copyOfWindowLocation)
console.log("Javascript structure - copy:", copyOfWindowLocation)

In 2023 there is now a official JavaScript way for deep clone of objects structuredClone()

But I recommend you to read the documentation and do a few tests because certain clones don't work. https://developer.mozilla.org/en-US/docs/Web/API/structuredClone

For example this throws a error:

var clone = structuredClone(window.location) 

The error

VM673:1 Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': Location object could not be cloned.
at <anonymous>:1:13

Upvotes: 21

Vishal
Vishal

Reputation: 1414

Object.assign create a Shallow Copy only.

const originalObject = {
        api : 'POST',
        contentType : 'JSON',
        userData : {
            name : 'Triver',
            email : '[email protected]'
        },
    responseTime: '10ms'
}

const originalObjectRef = Object.assign({}, originalObject);
originalObjectRef.contentType = 'XHTML';
originalObjectRef.userData.name = 'Red John';
console.log(originalObject);

Output:
{
    "api": "POST",
    "contentType": "JSON",
    "userData": {
        "name": "Red John",
        "email": "[email protected]"
    },
    "responseTime": "10ms"
}

In shallow copy, a reference variable mainly stores the address of the object it refers to. When a new reference variable is assigned the value of the old reference variable, the address stored in the old reference variable is copied into the new one. This means both the old and new reference variable point to the same object in memory. As a result if the state of the object changes through any of the reference variables it is reflected for both.

Note: Below is the ES6 way of shallow copy.

const originalObjectRef = {...originalObject};

Hope this might help someone, Thanks.

Upvotes: 4

terrymorse
terrymorse

Reputation: 7086

As mentioned above, Object.assign() will do a shallow clone, fail to copy the source object's custom methods, and fail to copy properties with enumerable: false.

Preserving methods and non-enumerable properties takes more code, but not much more.

This will do a shallow clone of an array or object, copying the source's methods and all properties:

function shallowClone(src) {
  let dest = (src instanceof Array) ? [] : {};

// duplicate prototypes of the source
  Object.setPrototypeOf(dest, Object.getPrototypeOf(src));

  Object.getOwnPropertyNames(src).forEach(name => {
    const descriptor = Object.getOwnPropertyDescriptor(src, name);
    Object.defineProperty(dest, name, descriptor);
  });
  return dest;
}

Example:

class Custom extends Object {
  myCustom() {}
}

const source = new Custom();
source.foo = "this is foo";
Object.defineProperty(source, "nonEnum", {
  value: "do not enumerate",
  enumerable: false
});
Object.defineProperty(source, "nonWrite", {
  value: "do not write",
  writable: false
});
Object.defineProperty(source, "nonConfig", {
  value: "do not config",
  configurable: false
});

let clone = shallowClone(source);

console.log("source.nonEnum:",source.nonEnum);
// source.nonEnum: "do not enumerate"
console.log("clone.nonEnum:", clone.nonEnum);
// clone.nonEnum: – "do not enumerate"

console.log("typeof source.myCustom:", typeof source.myCustom);
// typeof source.myCustom: – "function"
console.log("typeof clone.myCustom:", typeof clone.myCustom);
// typeof clone.myCustom: – "function"

jsfiddle

Upvotes: 1

Saqib Naseeb
Saqib Naseeb

Reputation: 741

var copy = Object.assign({}, originalObject);

does a shallow copy which is changing the copy reflect changes in your original object also. So to perform deep copy I would recommend the lodash cloneDeep

import cloneDeep from 'lodash/cloneDeep';
var copy = cloneDeep(originalObject);

Upvotes: 2

Ramanlfc
Ramanlfc

Reputation: 8354

Forget about deep copy, even shallow copy isn't safe, if the object you're copying has a property with enumerable attribute set to false.

MDN :

The Object.assign() method only copies enumerable and own properties from a source object to a target object

take this example

var o = {};

Object.defineProperty(o,'x',{enumerable: false,value : 15});

var ob={}; 
Object.assign(ob,o);

console.log(o.x); // 15
console.log(ob.x); // undefined

Upvotes: 39

Bihn Kim
Bihn Kim

Reputation: 314

It creates a shallow copy, according to this paragraph from MDN:

For deep cloning, we need to use other alternatives because Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value.

For the purposes of redux, Object.assign() is sufficient because the state of a redux app only contains immutable values (JSON).

Upvotes: 14

Rish
Rish

Reputation: 1383

By using Object.assign(), you are actually doing Shallow Copy of your object. Whenever we do an operation like assigning one object to other, we actually perform a shallow copy, i.e. if OBJ1 is an object, modifying it through another object which is OBJ2 will reflect changes in OBJ1 too.

Upvotes: 40

Related Questions