1

Suppose we have two objects:

var a = { foo: { bar: 1 } }
var b = { foo: { bar: 2 } }

If I set the object b to a (a = b), I expect that a takes the value of b, not the reference. So, in this case:

a = b
a.foo.bar = 3
console.log(b.foo.bar);

I expect that the last console.log shows 2, not 3. Why? Just because I changed a property related to a, not to b.

I can't understand why JavaScript also changes the b property and how to avoid this unexpected behaviour.

How to avoid this behaviour? Should I assign object to variables in a different way?

SyncroIT
  • 1,510
  • 1
  • 14
  • 26
  • 3
    Your expectation is incorrect. See https://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language – bugs Oct 05 '18 at 10:09
  • The "issue" here is that when you do `a = b` you are actually giving the reference to `b` to the variable `a` which means that those 2 variables point to the same object, which if you change one, you change both you could read [this article](https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0) for more JS tweaks – Alejandro Vales Oct 05 '18 at 10:09
  • a=b will assign the reference of b object to a object – Suresh Oct 05 '18 at 10:10
  • Ok, and why if I change a property that's inside the object it doesn't change on the other object? Example:https://pastebin.com/yKniYGW1 – SyncroIT Oct 05 '18 at 10:13
  • @Syncro Because after `a = b`, there is no "other" object anymore. – T.J. Crowder Oct 05 '18 at 10:16
  • @Syncro I tried your sample and it changes, console.log displays 'foo' – Stwosch Oct 05 '18 at 10:18
  • @Stwosch I'm so sorry, my fault. In my project it didn't change.. – SyncroIT Oct 05 '18 at 10:21
  • @StefanBlamberg - No, this has nothing to do with pass-by-value/pass-by-reference (which refer to passing arguments into functions). – T.J. Crowder Oct 05 '18 at 10:21

1 Answers1

5

...I expect that a takes the value of b, not the reference...

That's incorrect. Variables contain values. In the case of an object, the value is an "object reference" which tells the JavaScript engine where the object is (elsewhere) in memory.¹ So a = b makes a "point" to the same object b "points" to. If you change that object's properties, you'll observe those changes regardless of the variable you get the reference from.

After your initial setup, you have something like this in memory (various details omitted):

               +−−−−−−−−−−−−−−+
a: Ref5465−−−−>|   (object)   |
               +−−−−−−−−−−−−−−+     +−−−−−−−−−−+
               | foo: Ref8761 |−−−−>| (object) |
               +−−−−−−−−−−−−−−+     +−−−−−−−−−−+
                                    | bar: 1   |
                                    +−−−−−−−−−−+

               +−−−−−−−−−−−−−−+
b: Ref1574−−−−>|   (object)   |
               +−−−−−−−−−−−−−−+     +−−−−−−−−−−+
               | foo: Ref4456 |−−−−>| (object) |
               +−−−−−−−−−−−−−−+     +−−−−−−−−−−+
                                    | bar: 2   |
                                    +−−−−−−−−−−+

(Those "ref" values are conceptual, of course; we never actually see them.)

then when you do a = b, the object that a used to refer to is eligible for garbage collection and you have this instead:

a: Ref1574−−+
            |
            |
            |  +−−−−−−−−−−−−−−+
            +−>|   (object)   |
            |  +−−−−−−−−−−−−−−+     +−−−−−−−−−−+
            |  | foo: Ref4456 |−−−−>| (object) |
            |  +−−−−−−−−−−−−−−+     +−−−−−−−−−−+
b: Ref1574−−+                       | bar: 2   |
                                    +−−−−−−−−−−+

Notice how a now has the same "ref" value b does. So naturally, a.foo.bar = 3 changes that one bar property both a and b (indirectly) point to.

If you want to make a copy of an object, you can make a shallow copy with a = Object.assign({}, b) or (in ES2018+) a = {...b}. But if you want to copy the object foo refers to, you'll need a deep copy; see this question's answers.


¹ Some people, particularly in academic contexts, would say that the object itself is the value and that the "object reference" thing is a low-level plumbing concept, and the only reason we notice a difference between objects and, say, numbers is that objects are mutable. I understand their point, but I take a pragmatic approach to it instead.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 2
    plus 1 for the diagrams – bugs Oct 05 '18 at 10:18
  • @T.J. Crowder, this behaviour also applies to PHP? I mean: if I assign a class istance to another var and then I assign a class property value, will PHP apply the same JS behaviour (referencing internal properties)? – SyncroIT Oct 06 '18 at 13:59
  • @Syncro - Yes, PHP does the same thing. This is how most object-oriented languages work (there are exceptions, but they're relatively rare): Variables contain *references* to objects, and assigning the value of a variable to another makes a copy of the reference, not the object. – T.J. Crowder Oct 06 '18 at 14:09
  • @Syncro - I should note that C++ is an exception, because in C++ if you want the behavior above, you use a pointer to the object rather than the object itself. So if you have class `Foo` with a field called `value`, `Foo f1(1); Foo f2 = f1; f2.value = 2; cout << f1.value << endl << f2.value << endl;` will show `1` and `2`, not `2` twice. But `Foo f(1); Foo* f1 = &f; Foo* f2 = f1; f2->value = 2; cout << f1->value << endl << f2->value << endl;` will show `2` twice because `f1` and `f2` are both pointers to `f`. – T.J. Crowder Oct 06 '18 at 14:33
  • Let's say that C++ is more "technical" and you have more control on the software you write. I mean that you define the behaviour while in JS/PHP most behaviours are "hardcoded". Right? – SyncroIT Oct 06 '18 at 14:37
  • 1
    @Syncro - C++ does give you some control over certain things most other languages don't because of the complexity cost. – T.J. Crowder Oct 06 '18 at 14:50