Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clone object in JavaScript [duplicate]

Consider the below code or check this fiddle.

var obj = {
    name: "abc",
    age: 20
}

var objTwo;

console.log(obj.age);
objTwo = obj;
objTwo.age = 10;
console.log(obj.age);

I have created an object with name obj and it has two properties. Now I assign obj to another object named objTwo. Now I update one of the properties in objTwo. The same change is reflecting on obj as well. How can I assign values from one object to another without creating reference?

like image 948
SharpCoder Avatar asked May 20 '14 11:05

SharpCoder


People also ask

How many ways we can clone object in JavaScript?

JavaScript provides 3 good ways to clone objects: using spread operator, rest operator and Object. assign() function. Aside from just cloning objects, using object spread and Object. assign() lets you add or updated properties when creating the clone.

Does JavaScript support object cloning?

The javaScript, generally, we have used some default method/function to achieve. The goal for these types of concepts is called cloning the object, but each and every function has its own attributes and properties in the script.

What is the most efficient way to deep clone an object in JavaScript?

Spread operation is the easiest way to clone a object in ES6. Data loss happens on this method as well. However, since this is native to ES6 it is more performant than JSON. strigify().

How do you deep copy an object in JavaScript?

Copy an Object With Object.assign() was the most popular way to deep copy an object. Object. assign() will copy everything into the new object, including any functions. Mutating the copied object also doesn't affect the original object.


4 Answers

this will assign not by reference

<script>
    var obj = 
    {
        name: 'abc',
        age: '30'
    };

    var objTwo = {};

    for( var i in obj )
    {
        objTwo[i] = obj[i];
    }
</script>

view fiddle

like image 131
Hawk Avatar answered Oct 21 '22 14:10

Hawk


I would use jQuery to do this:

var obj1 = {
     name: "abc",
     age: 20    
}

console.log(obj1);

var obj2 = $.extend({}, obj1, {});
console.log(obj2);

obj2.age = 1;
console.log(obj2);

console.log(obj1);
like image 44
Shane T Avatar answered Oct 21 '22 13:10

Shane T


If you only need to clone simple objects, simply doing

JSON.parse (JSON.stringify (obj))

would suffice.

But this obviously doesn't work in all cases, since JSON.stringify can't handle circular references and strips out functions.

So if you want to go beyond that, things will get more complicated and you have to either rely on some utility library or need to implement your own deep clone method.

Here is a sample implementation that expects a level of deepness to clone.

(function (Object, Array) {
    function cloneObject(deep, scope, clonedScope) {
        var type = typeof this,
            clone = {},
            isCR = -1;

        deep = Number(deep) || 0;
        scope = scope || [];
        clonedScope = clonedScope || [];

        if (!Array.isArray(scope) || !Array.isArray(clonedScope) || clonedScope.length !== scope.length) {
            throw new TypeError("Unexpected input");
        }
        //If we find a primitive, we reeturn its value.
        if (type !== "object") {
            return this.valueOf();
        }

        scope.push(this);
        clonedScope.push(clone);

        if (0 === deep) { //If we reached the recursion limit, we can perform a shallow copy
            for (var prop in this) {
                clone[prop] = this[prop];
            }
        } else { //Otherwise we need to make some checks first.
            for (var prop in this) {
                if ((isCR = scope.indexOf(this[prop])) > -1) { //If we find a circular reference, we want create a new circular reference to the cloned version.
                    clone[prop] = clonedScope[isCR];
                } else if (typeof this[prop] !== "undefined" && this[prop] !== null) { //Otherwise continue cloning.
                    clone[prop] = (typeof this[prop] !== "object" ? this[prop] : this[prop].clone(deep - 1, scope, clonedScope)); //If we find a non object, we can directly assign it. Otherwise we need to recursively call the clone function, counting down the limit, and injecting the scopeArrays, to find circular references.
                } else { //If the property is undefined or null, assign it as such.
                    clone[prop] = this[prop];
                }
            }
        }

        scope.pop(); //If we leave a recursion leve, we remove the current object from the list.
        clonedScope.pop();

        return clone;
    }


    function cloneArray(deep, scope, clonedScope) {
        var clone = [];

        deep = Number(deep) || 0;

        scope = scope || [];
        clonedScope = clonedScope || [];

        if (!Array.isArray(scope) || !Array.isArray(clonedScope) || clonedScope.length !== scope.length) {
            throw new TypeError("Unexpected input");
        }


        scope.push(this);
        clonedScope.push(this);

        if (0 === deep) clone = this.concat();
        else this.forEach(function (e) {
            if ((isCR = scope.indexOf(e)) > -1) {
                clone.push(clonedScope[isCR]);
            } else if (typeof e !== "undefined" && e !== null) {
                clone.push((typeof e !== "object" ? e : e.clone(deep - 1, scope, clonedScope)));
            } else {
                clone.push(e);
            }
        });

        scope.pop();
        clonedScope.pop();

        return clone;
    }

    Object.defineProperty(Object.prototype, "clone", {
        enumerable: false,
        value: cloneObject
    });

    Object.defineProperty(Array.prototype, "clone", {
        enumerable: false,
        value: cloneArray
    });

})(Object, Array);

Note that extending the the built-ins prototypes is often frowned upon, however i decided to do it that way to avoid an additional typecheck and to split the logic for arrays and objects a bit more. This can easily be refactored to a normal function instead

Some tests to check that we indeed have new references.

var first = {
    a: {
        b: "b",
        c: {

        }
    },
    b: "asd",
    c: [{}],
    d: undefined,
    e: null,
    f: function a() {} //functions keep their original reference..
};

first.a.c.a = first.a; //Circular object reference
first.c.push(first.c); //Circular array reference

var second = first.clone(Infinity);

console.log(second, second.a === second.a.c.a, first.a !== second.a.c.a); //..., true, true.

There may be a lot of space for improvement, I particularily don't like how the scope and clonedScope get's injected. If anyone has an better idea of finding and reattaching circular references, i'd be glad to update the answer

Here is a Fiddle as well.

like image 32
Moritz Roessler Avatar answered Oct 21 '22 13:10

Moritz Roessler


var obj, objTwo;

obj = {
    name: "abc",
    age: 20
}
console.log(obj.age);
objTwo = copy(obj);
objTwo.age = 10;
console.log(obj.age);

function copy (obj) {
    var key, rtn = Object.create(Object.getPrototypeOf(obj));
    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            rtn[key] = obj[key];
        }
    }
    return rtn;
}

In javascript everything is passed by reference. The reason the modification "leaks" to the original function property (variable), is because you are modifying the object's property, not the object (reference) itself. Copy the object to another variable, instead of re-assigning it.

Off-topic:

In javascript everything is passed by reference. apparently is a bit contested; That's why I'm adding this addendum. Feel free to correct me if I'm wrong.

Is JavaScript a pass-by-reference or pass-by-value language? The top answer states it most clearly:

Instead, the situation is that the item passed in is passed by value. But the item that is passed by value is itself a reference.

So my wording was confusing, but if you also keep in my mind that every operator returns a reference, and keep in mind that every assignment and parameter-passing simply copies those references returned by operators (and value-literals), then passed-by-reference kind of makes sense.

The linked top answer has the full (correct) explanation.

like image 39
dot slash hack Avatar answered Oct 21 '22 12:10

dot slash hack