1

I'm having a strange situation in JavaScript where I am creating an Object and then passing it as an Argument to a Function which then updates the values and then returns a new updated Object.

function objBuild() {

    var obj_old = {}; // original object
    var obj_new = {}; // updated object

    // set values for original object
    obj_old.val01 = "val01_old";
    obj_old.val02 = "val02_old";
    obj_old.val03 = "val03_old";

    // set values for new object using
    // the original object as a template
    obj_new = objUpdate(obj_old);

    console.log(obj_old); // this shows obj_new data which I don't want
    console.log(obj_new); // this shows obj_new data which I want

}

function objUpdate(obj) {

    obj.val03 = "val03_new";

    return obj;

}

I was expecting the new Object to be updated with the new Value, which it is, however the old Object is updated as well.

Maybe the function is taking the old Object as a reference and even though I'm returning a separate value it remembers what's happened to it?

I'm not sure but if that is the case is it possible to keep the old Object intact?

TheLovelySausage
  • 3,838
  • 15
  • 56
  • 106
  • _I was expecting the new Object to be updated with the new Value, which it is, however the old Object is updated as well._ Objects, in javascript, are passed by "reference". Any non-primitive value technically is (so arrays, for example, have the same behavior). You need to **clone** the object first and alter the **cloned object** values, it's not implicit, and that's by design. – briosheje Jun 27 '19 at 09:55
  • "*even though I'm returning a separate value*" - where would that be? No, there is no separate object. – Bergi Jun 27 '19 at 10:32

3 Answers3

1

You're not actually creating a new object, you're setting the old object to the new object. So you're correct, the old object value is still being referenced.

If you want to make a new object, and you don't have any nested objects, I would use Object.assign(). This makes a shallow copy of the object. You could do something like this:

obj_new = objUpdate(Object.assign({}, obj_old));

This will create a new object with the enumerable properties of the old object.

If you have nested objects, these will still be copied by reference, so I would loop over the object and copy the properties that way.

JoshG
  • 6,472
  • 2
  • 38
  • 61
1

Remember that objects, including arrays are passed by reference while strings, booleans and numbers are passed by value.

Here, you are passing object(by reference) so it is modifying the values of old object. Both old and new objects are pointing to the same value.

function objBuild() {

    var obj_old = {}; // original object
    var obj_new = {}; // updated object

    // set values for original object
    obj_old.val01 = "val01_old";
    obj_old.val02 = "val02_old";
    obj_old.val03 = "val03_old";

    // set values for new object using
    // the original object as a template
    obj_new = objUpdate(obj_old);

    console.log(obj_old); // this shows obj_new data which I don't want
    console.log(obj_new); // this shows obj_new data which I want

}

function objUpdate(obj_old) {
    var obj = JSON.parse(JSON.stringify(obj_old));
    obj.val03 = "val03_new";

    return obj;
}

objBuild();
Swapnil S.
  • 13
  • 5
1

Please read: Is JavaScript a pass-by-reference or pass-by-value language?

In javascript, objects are "technically" passed by "reference". Hence, the original value is altered, because obj is, in fact, the original object.

You need to clone the object, and there is a vast scenario about "how" you should do that, since object may be shallow copied or deep copied. Read more about that here: What is the most efficient way to deep clone an object in JavaScript? and here: How do I correctly clone a JavaScript object?

Anyway, to fix your issue, just use Object.assign on a brand new object to copy the values of the original object, for your case, it's just enough, despite I recommend you to read the above posts to learn when and how to properly copy objects.

function objBuild() {

    var obj_old = {}; // original object
    var obj_new = {}; // updated object

    // set values for original object
    obj_old.val01 = "val01_old";
    obj_old.val02 = "val02_old";
    obj_old.val03 = "val03_old";

    // set values for new object using
    // the original object as a template
    obj_new = objUpdate(obj_old);

    console.log(obj_old); // this shows obj_new data which I don't want
    console.log(obj_new); // this shows obj_new data which I want

}

function objUpdate(obj) {
    var _cloned = Object.assign({}, obj);
    _cloned.val03 = "val03_new";

    return _cloned;

}

objBuild();
briosheje
  • 7,356
  • 2
  • 32
  • 54
  • This is such a nice, elegant solution, I never knew about Object cloning before. Just so that I understand your answer though, this only happens to Objects and Arrays? If so, is there an equivalent Object.assign command for an array? – TheLovelySausage Jun 27 '19 at 10:09
  • 1
    Actually, no, functions are passed by reference as well, as many other things. In general, **only primitives** are passed by value, so you can safely think that numbers, strings and booleans are, while arrays, objects and functions actually aren't. About cloning, keep in mind that you should think about **how** you clone the object. In the linked posts, there are a lot of informations about how shallow copy works against deep copy (deep copy means cloning every property of the object, including prototypes, getters, setters). For instance, you can go safe with object.assign, though. – briosheje Jun 27 '19 at 10:11
  • 1
    Also, as a "final" side note, it's technically incorrect to say that these are passed by reference. Under the hood, there is much more happening, though you can consider the concept pretty similar to "pass by reference", despite it really isn't exactly that. – briosheje Jun 27 '19 at 10:12
  • 1
    About arrays: yes, you can just use `array.slice()`: https://jsfiddle.net/gqszwp4m/ ... Or, as always, many other alternatives, that's just what came to my mind first ;) – briosheje Jun 27 '19 at 10:13