0

In Javascript, when I use the extend function in the underscore.js library... can somebody please conceptually / visually describe to me what happens in the back-end in regards with memory - with this example:

var obj = {hello: [2]};
var obj2 = {hola: [4]};
_.extend(obj, obj2)
obj2.hola = 5;
console.log(obj) // hola still has a value of `[4]`

My issue is if I console.log(obj), for some reason, hola still has a value of [4]. I fully thought I would get the value 5 (via pass by reference)...

For the above example, here is what is visually / conceptually going on in my head:

  1. The extend function deeply copies the key hola into obj:

    obj = {hello: [2], hola: TBD} ]btw - is obj storing only one memory address for this object?

  2. Then I suspect that hola stores a memory address to the value of [4] (so at this point I suspect obj would be

    obj = {hello: [2], hola: #0x93490234}

Which is why I fully expected to see a 5 under obj. Can you tell me what's wrong with my visualization above?

Lastly, with the explanation, can you point out how the above example is any different than the following example (I understand how / why the below example works - just not the above example and would like to hear why the below works and the above doesn't).

var obj2 = {hola: [4]};
var obj = obj2;
obj2.hola = 5; //console.log(obj) will say that hola equals 5
dlearns
  • 51
  • 1
  • 7
  • 1
    All arguments in JavaScript are passed by value, all the time. **[Read this](https://stackoverflow.com/questions/42045586/whats-the-difference-between-a-boolean-as-primitive-and-a-boolean-as-property-o/42045636#42045636)** for explanation. – Scott Marcus Jul 06 '18 at 22:15
  • Thanks @ScottMarcus - I read the explanation in your link, but it didn't quite help me. I don't have a function in my example / no arguments. I'm not sure how it relates to my question - any guidance would be appreciated... – dlearns Jul 06 '18 at 22:19
  • And, where exactly did you insert the `console.log()`? – Scott Marcus Jul 06 '18 at 22:20
  • 1
    You do have a function in your example `.extend()` and you are passing arguments to it. Those arguments are being passed by value. – Scott Marcus Jul 06 '18 at 22:21
  • 1
    `_.extend` **copies the properties** that `obj2` has *at that moment* to `obj`. Changing `obj2` *afterwards* has no effect on `obj`. – Felix Kling Jul 06 '18 at 22:24
  • *" I suspect that hola stores a memory address to the value of [4]"* That is correct. But `obj2.hola = 5;` basically assigns a new memory address to a different value. `obj.hola` still has the address of the "old" value. – Felix Kling Jul 06 '18 at 22:26
  • *"how the above example is any different than the following example"* Copying properties from one object to another is different than assigning the same object to a different variable. – Felix Kling Jul 06 '18 at 22:27
  • @scottmarcus I'm sorry - you are right - I do have a function .extend on there. I'm still not understanding how it relates to what you are stating. So yes, when extend receives the obj2 parameters - it's saving a memory address to obj2 (not the value - but the memory address... so then what?) why wouldn't obj.hola = 5 then? – dlearns Jul 06 '18 at 22:28
  • `obj` doesn't have a reference to `obj2`. The *values of the properties* of `obj2` are *copied* to `obj`. – Felix Kling Jul 06 '18 at 22:31
  • @FelixKling, thank you - however, I believe "changing obj2 afterwards" does have an effect on obj - say instead of obj2.hola = 5, I changed it to obj2.hola[0] = 5.... then if I console.log(obj) it will say that the value did change to [5]; – dlearns Jul 06 '18 at 22:32
  • 1
    I was just trying to help you understand that there is no such thing as pass by reference in JavaScript. – Scott Marcus Jul 06 '18 at 22:33
  • You are not changing `obj2` in that case. You are changing (mutating) the array that is assigned to `obj2.hola` (which is also referenced by `obj.hola`). Big difference! You might as well do `var arr = obj.hola; arr.push(42);` and would see a similar result. – Felix Kling Jul 06 '18 at 22:33
  • Your use case can be simplified to the following example: `var foo = 1; var bar = foo; foo = 2;`. I copy the value of `foo` to `bar`. Assigning *a new value* to `foo` does not magically change `bar`. – Felix Kling Jul 06 '18 at 22:37
  • @FelixKling, I'm really sorry - I hear what you are saying and I really want to understand (no doubt - it's me, not you)... but it's not making sense to me - I'm a very visual person - and I just made this diagram - and I'm wondering if you can see where I am visualizing this wrong: https://imgur.com/a/yG8ZxIR – dlearns Jul 06 '18 at 23:04
  • In your image, `hola: 0x00239245` is wrong. That would point back to the object. Instead, both `hola`s are `[4]` (or rather the address of `[4]`, which you do not have in your image). – Felix Kling Jul 06 '18 at 23:07
  • You seem to think that the property `obj.hola` somehow points to the property `obj2.hola`. But that's not the case. `obj.hola` has a *copy of the value* of `obj2.hola`. I don't think I can explain it better than I did in my answer, but I encourage you to forget about properties first and just consider variables. Properties are like variables. – Felix Kling Jul 06 '18 at 23:09
  • @FelixKling - thank you - this helps a lot. – dlearns Jul 07 '18 at 00:00

1 Answers1

-1

Maybe this helps:

Lets distinguish between two concepts first: values and bindings. A value is a piece of data, like a string, number, boolean, object, etc. Every value is stored in memory and has an address.

A binding is like a container or label that holds an address.

  • when assigning a value to a binding we are assigning the address of the value
  • when accessing a binding we get the value stored at that address

Example:

var a = 42;

a is a binding, 42 is a value. Lets say 42 is stored in memory at 0x1, then a really holds the address 0x1.

When we are trying to read the value, e.g.

console.log(a);

we need to look at address 0x1 to get the actual value.

Now what happens when we "assign a binding to another binding", i.e. bar = foo?

var foo = 42; // 0x2
var bar = foo;

42 is stored at 0x2. So foo holds the address 0x2. When we assign a binding to another one, we simply copy the address that the binding holds. So after var bar = foo;, both foo and bar hold the address 0x2. If I assign a new value to foo, e.g.

foo = 21;

then I'm making foo hold a new address (e.g. 0x3).

This does not change the address that bar holds.

Exactly the same applies to object properties.


Mutable values

Now you might wonder "why does foo and bar both change in the following example:

var foo = [42];
var bar = foo;
foo.push(21);
// bar also has [42, 21]

Lets assume [42] is stored at memory location 0x4.

Here is how it is different to the previous example:

We did not assign a new value to foo.

Instead we referenced the array (at address 0x4) through foo and mutated it. Mutable values are values that can be changed in place, i.e. the bits at that memory location can change.

After calling foo.push, foo still holds the address 0x4 and so does bar.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143