11

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?

Scimonster
  • 32,893
  • 9
  • 77
  • 89
SharpCoder
  • 18,279
  • 43
  • 153
  • 249
  • 1
    "assign without reference" is called "clone" in javascript and similar languages. See [this question](http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object) for possible implementations. – gog May 20 '14 at 11:40
  • @georg: Thanks. Clone is the right word. I missed it – SharpCoder May 20 '14 at 11:41
  • @georg—the accepted answer there isn't a particularly good answer, it essentially says "use jQuery". – RobG May 20 '14 at 11:53
  • @RobG: I nowadays have problem with people using Jquery too much for something plain javascript can do. Too much dependency on libraries is really detrimental to programmatical thinking. – Jack_of_All_Trades May 20 '14 at 12:33
  • Just use the jQuery extend method as I mentioned here: http://stackoverflow.com/a/23759239/3622881 – Shane T May 20 '14 at 12:58

5 Answers5

2

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

Hawk
  • 788
  • 1
  • 6
  • 18
  • 2
    You should have a [*hasOwnProperty*](http://ecma-international.org/ecma-262/5.1/#sec-15.2.4.5) filter (or use [*Object.keys*](http://ecma-international.org/ecma-262/5.1/#sec-15.2.3.14)), the above will copy enumerable inherited properties too. – RobG May 20 '14 at 11:55
  • thanks for the new info..makes my coding better.. @RobG – Hawk May 20 '14 at 11:58
  • @RobG: But it's not necessary for plain objects like in this example. Only if you did implement a generic function that might once be used to clone fancy instances you'd take care of that. – Bergi May 20 '14 at 12:45
  • @Bergi—I assumed it's just an example, I think the OP will quickly move to more complex scenarios. Copying or cloning objects can only be done within certain parameters, there is no general solution. The answers to the question linked to RGraham's comment probably cover it, but there's no single answer that really does the job. – RobG May 20 '14 at 12:59
1
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.

Community
  • 1
  • 1
dot slash hack
  • 558
  • 1
  • 5
  • 13
  • 6
    `Unhandled Error: Object.defineProperties: property descriptor is not object` – Bergi May 20 '14 at 11:44
  • @Bergi Thanks for pointing that out. Stupid mistake on my part. @ Jack_of_All_Trades Could be true, some constructive arguments would be appreciated. I'm ignoring that everything you said is in CAPS, I'm not interested. – dot slash hack May 20 '14 at 12:12
  • 1
    @KemHeyndels: I apologize for that. It was intended for humor. Sorry again for that. Here is the link I have attached that might help to rectify my ignorant behavior.Javascript passes by value for the primitive types and by reference to object types.The link might provide more information. http://snook.ca/archives/javascript/javascript_pass – Jack_of_All_Trades May 20 '14 at 12:19
  • Why do you do `Object.create(null)` now? Using the same prototype was completely fine. – Bergi May 20 '14 at 12:42
  • @Bergi That's just my personal bias (rarely use prototypes). I'll bring it back, probably more reliable anyway. – dot slash hack May 20 '14 at 12:43
  • @Jack_of_All_Trades Thanks for the link. The problem the article describes is correctly dealt with. I think we have a conceptual difference, I added an off-topic section where I try to explain it. :) – dot slash hack May 20 '14 at 13:04
1

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);
Shane T
  • 51
  • 3
1

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.

Moritz Roessler
  • 8,542
  • 26
  • 51
0

Okay, This might be a strange solution but it is plain javascript. If you want to clone something, I would go with the word, "deep copy", you can use JSON like this:

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


new_object=JSON.parse(JSON.stringify(obj));

Now, you have clone of the object obj.

Another solution is like this:

var new_obj={};
for( new_prop in obj){
    if (obj.hasOwnProperty(new_prop)){
          new_obj[new_prop]=obj[new_prop]
     }
 }
Jack_of_All_Trades
  • 10,942
  • 18
  • 58
  • 88