0

How is it possible to create an instant of an object in JavaScript which doesn't affect to any other instance of that object, here is an example:

var A = {
        b: "2", 
        c: "3", 
        d: {
            e: "5", 
            f:{
                    g: "7"
                },
            h: function(){
               return "8";
              }
            }
}
var a1 = Object.create(A);
var a2 = Object.create(A);
a1.d.f.g = 10;
console.log(a2.d.f.g);//Result is 10 while it was supposed to be 7

In the example above when a1.d.f.g is changed, a2.d.f.g is changed too. What's the matter?

I even tried Object.assign({}, A), but the problem still remains.

var a1 = Object.assign({}, A);
var a2 = Object.assign({}, A);

Update: Thanks to some answers which gave me the clue, after searching a lot I figured that there are two concepts related to object copy in JavaScript, Shallow Copy and Deep Copy, in shallow copy only top-level properties are copied but reference object properties in further levels will be shared, if you want to have a totally separate instance you need deep copy or deep clone.

A shallow copy successfully copies primitive types like numbers and strings, but any object reference will not be recursively copied, but instead the new, copied object will reference the same object.

If an object references other objects, when performing a shallow copy of the object, you copy the references to the external objects.

When performing a deep copy, those external objects are copied as well, so the new, cloned object is completely independent from the old one. Source

Personally I think shallow and deep copy concepts in JavaScript seem weird, doesn't make sense, but it's what it is, plus, sort of similar stuff exists in C# too. Object.assign() and spread operator make shallow copy so you'll need another function to make a deep clone for you.

Some people advise using JSON.parse(JSON.stringify(obj)) but it doesn't work for properties that are function itself for example a1.d.h() in the sample above is not valid if you use JSON solution, moreover the type might change in some cases as it occurs for Date type for instance.

Some libraries are recommended but in my case I prefer not to use any third party. I figured out it's better to use a loop through reference object properties and create a deep clone, here is the method that I'm talking about:

function deepClone (obj) {
    var _out = new obj.constructor;

    var getType = function (n) {
        return Object.prototype.toString.call(n).slice(8, -1);
    }

    for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
            _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
        }
    }
    return _out;
}

So it will become something like this eventually:

var a1 = deepClone(A);
var a2 = deepClone(A);
a1.d.f.g = 10;
console.log(a2.d.f.g);//Result is 7 now
Muhammad Musavi
  • 2,512
  • 2
  • 22
  • 35

1 Answers1

3

You need to create a deep clone, you can use JSON.parse(JSON.stringify()) to deep clone

var A = {
        b: "2", 
        c: "3", 
        d: {
            e: "5", 
            f:{
                    g: "7"
                }
            }
}
var a1 = JSON.parse(JSON.stringify(A));
var a2 = JSON.parse(JSON.stringify(A));
a1.d.f.g = 10;
console.log(a2.d.f.g);
Code Maniac
  • 37,143
  • 5
  • 39
  • 60