0

Please explain this phenomenon to me.

this.testObject = { a: 1, b: { a: 3, b: { a: 78, b: null } } };

let g = this.testObject;

while (g.b) {
  g = g.b;
}

g.b = {
  a: 79,
  b: null
};

console.log(this.testObject); // { a: 1, b: { a: 3, b: { a: 78, b: { a: 79, b: null } } } }

Now I decided to stick with this.testObject and removed g:

this.testObject = { a: 1, b: { a: 3, b: { a: 78, b: null } } };

while (this.testObject.b) {
  this.testObject = this.testObject.b;
}

this.testObject.b = {
  a: 79,
  b: null
};
console.log(this.testObject); // {a": 78,b": {a": 79,b": null}

What’s going on? How is my assignment of g affecting this object this way?

In first snippet, if I did g=g.b then I am basically assigning a new value to g. However if I explicitly do this, then result isn't same?

this.testObject = {a:1, b:{a:3,b:{a:78, b:null}}};
let g = this.testObject;

while(g.b){
    g = {a:7777, b:null};
}

g.b={a:79, b:null};
this.testObject; // {a": 1,b": {a": 3,b": {a": 78,b": null}
TrickOrTreat
  • 821
  • 1
  • 9
  • 23
  • 2
    ?? Your loop clearly *changes* `this.testObject` to a series of new values. Nothing changes it back to its original value. – Pointy May 10 '21 at 12:18
  • 1
    What is the expected bevhavior? – Niklas Gschaider May 10 '21 at 12:20
  • Yes but I did the same when i wrote `let g = this.testObject;` How come it is accepting phenomena at line 6 in first phase differently compared to line 5 in phase 2? – TrickOrTreat May 10 '21 at 12:20
  • 3
    `g` and `this.testObject` are two separate modifiable things (`g` is a variable and `this.testObject` is an object property). Changing `g` does **not** change the value of `this.testObject`. – Pointy May 10 '21 at 12:23
  • Oh so javascript is using the very fact that object's property of being mutable and g is just a shallow copy here? – TrickOrTreat May 10 '21 at 12:28
  • 1
    @TrickOrTreat `g` is _not_ a copy. It’s just a reference. Reassigning `g` does not affect the previously assigned reference. – Sebastian Simon May 10 '21 at 12:34
  • 1
    See [Variable re-assignment of object reference leaves other object unaltered (no “transitive” assignment)](/q/41814414/4642212). – Sebastian Simon May 10 '21 at 12:39
  • But why `g = {a:7777, b:null};` is working different (as it's not considering `g.b={a:79, b:null};` ) but `g=g.b` . is able to include `g.b={a:79, b:null};` – TrickOrTreat May 10 '21 at 12:43

2 Answers2

2

I'm guessing you're expecting the two snippets to produce the same result. But the code doesn't do the same thing.

You're using g as a sort of reference in the first example. This means that every time you set g in the while loop, you're not affecting this.testObject in any way since you're just "moving" the reference. But it is still a reference, so altering anything inside the object that g is referencing, you will also set it in this.testObject, thus producing the result you get in the first example.

In the second example, you're replacing this.testObject each time in the while loop. So setting this.testObject = this.testObject.b will remove the outer object, resulting in this.testObject going from:

{ a: 1, b: { a: 3, b: { a: 78, b: null } } }

To:

{ a: 3, b: { a: 78, b: null } }

And since this.testObject.b isn't null, you're repeating this again, making this.testObject become:

{ a: 78, b: null }

Lastly, you're setting this.testObject.b to a new object, overwriting the previous null value. This results in the output you get in the second example.

Phoenix1355
  • 1,589
  • 11
  • 16
  • I just updated my fears in javascript code back again. Please have a look. – TrickOrTreat May 10 '21 at 12:37
  • But why `g = {a:7777, b:null};` is working different (as it's not considering `g.b={a:79, b:null};` ) but `g=g.b` . is able to include `g.b={a:79, b:null};` – TrickOrTreat May 10 '21 at 12:43
  • 1
    @TrickOrTreat Every time you write `g = ...`, you're reassigning what `g` is referencing to. With your new code example, you're first assigning `g` to point at `this.testObject`, but then you overwrite that reference with the new `{a:7777, b:null};`. Since you're moving the referencing, you're not altering `this.testObject` in any way. I'd suggest looking at [this article](https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0), which explains the difference between values and references in javascript. – Phoenix1355 May 10 '21 at 13:23
  • oh.ok. Thanks for this explaining. This helps. – TrickOrTreat May 10 '21 at 15:01
1

Usually when answering these question people draw boxes and arrows between them to indicate pointers, but I'm lazy, so let's consider a simple virtual machine instead. This machine can operate on memory blocks and their numbers (aka "pointers"). There are three operations:

  • allocate: allocate a memory block, initialize it with some data and return a pointer to it (denoted $number)
  • write: set the value of a variable or a property to a pointer, so that the variable "points" to the corresponding memory block
  • read: given a pointer, return the content of its memory block

In terms of this machine, your first snippet would translate to this:

// this.testObject = { a: 1, b: { a: 3, b: { a: 78, b: null } } };

allocate: $1 = {a: 78, b: null}
allocate: $2 = {a: 3, b: $1}
allocate: $3 = {a: 1, b: $2}

write: this.testObject = $3

// let g = this.testObject;

write: g = $3

// while (g.b) {  g = g.b; }

write: g = $3.b // first pass, g is now $2
write: g = $2.b // second pass, g is now $1

// g.b = { a: 79, b: null }

allocate: $4 = { a: 79, b: null }

write: $1.b = $4

// console.log(this.testObject);

read: $3 ==> {a: 1, b: $2}
read: $2 ==> {a: 3, b: $1}
read: $1 ==> {a: 78, b: $4}
read: $4 ==> { a: 79, b: null }

Basically, each {...} is an "allocate" operation, each assignment is a "write" and console.logs are "reads". Try to "translate" your other snippets to this machine and you'll see what's actually going on.

georg
  • 211,518
  • 52
  • 313
  • 390