3

I am confused about these two cases:

The first one:

let a: object = {'name': 'gavin'};
let b: object = {};
b['name'] = a['name']; // at this time. a and b are both {'name': 'gavin'};
delete a['name']; // now a is {} and b is {'name': 'gavin'}

The second one:

let a: object = {'tag': {'name': 'gavin'}};
let b: object = {};
b['tag'] = a['tag']; // at this time. a and b are both {'tag': {'name': 'gavin'}};
delete a['tag']['name']; // now a and b are both {'tag': {}}

In the second case, if I delete a['tag'], a will be {} and b will be {'tag': {'name': 'gavin'}}. As I expect, if I delete a['tag'], b will be null and if I delete a['tag']['name'], b will not be influenced. Why is such a result appears?

English isn't my native language, if I make a spelling error, don't hesitate to let me know it.

Patrick Hund
  • 19,163
  • 11
  • 66
  • 95
gavinSong
  • 215
  • 2
  • 12
  • Both `a.tag` and `b.tag` share the same reference to object `{'name': 'gavin'}`. So, `delete`-ing one is modifying the other. – Hassan Imam Dec 19 '17 at 07:36
  • Because `delete a.tag.name` does not affect `a` (or `b`) at all, it does affect the object which is referenced from both `a.tag` and `b.tag`. – Bergi Dec 19 '17 at 07:37

1 Answers1

4

In your first example, you are assigning a string b['name'] = a['name']. String's are passed by value in javascript, so effectively a new copy of that string is created in memory. a['name'] is not equal to b['name'] in terms of the actual in-memory location they are referencing.

You can think of this as having two copies of a string. When you delete the string, you delete one copy, but not the other.

In your second example, you are assinging an object b['tag'] = a['tag']. Objects are passed by reference in javascript which means you are not creating a new copy of the object, but instead assigning the same object to b['tag']. So a['name'] is the same object as b['name'].

You can think of this as having one copy of an object, where botha['tag'] and b['tag'] are using that single copy. When you delete the name property from that single copy, it will not appear in either a['tag'] or b['tag'] because they both use that same object.

The answer to this question might help you to understand better value vs object reference https://stackoverflow.com/a/37290849/845704

Edit

To perhaps put it in perspective, lets use your example with an additional variable. Keep in mind that this is no different in terms of data structures to your example, except that it's a few more lines of code.

let tag_obj = {'name': 'gavin'};
let a = { 'tag': tag_obj };
let b = {};
b['tag'] = a['tag'];

// a and b both now equal { tag: { name: 'gavin'} }

console.log(a.tag === tag_object) // True, it references tag object
console.log(b.tag === tag_object) // Also true, as it references the same object.

delete a.tag.name; 

Now if you delete a.tag, you are removing a property from the a object. Which shouldn't affect anything else, since a is not equal to anything else. When you delete a.tag.name however, you are deleting a property from the tag object, which we know in the example above as tag_object. Since both a.tag and b.tag reference the tag_object they will now both show with no name property.


There is no native 'operator' to make an additional copy of an object, but there are a few commonly used methods. The first is Object.assign which assigns the properties of one object to another object

let j = {'tag': {'name': 'james'}};
let k = j
console.log(k === j) // Will log true, they are the same object

let a = {'tag': {'name': 'gavin'}};
let b = Object.assign({}, a);
console.log(a === b) // Will log false, a new object has been created.

Now both of the objects are look the same. Unfortunately, Object.assign creates what is called a shallow copy. So the objects themselves are different, however any references inside the objects will be shared

let a = {'tag': {'name': 'gavin'}};
let b = Object.assign({}, a);
console.log(a.tag === b.tag) // Will log true, they share the same tag object

There are a few utility libraries around that will create deep copies of an object for you. You can also do this yourself recursively by running through the entire object tree.

However, a common, and reasonably fast approach to creating a deep copy of an object, is to use JSON.

let j = {'tag': {'name': 'james'}};
let k = JSON.parse(JSON.stringify(j));
console.log(j === k); // Will log false, a new object has been created.
console.log(j.tag === k.tag); // Will log false, a new object has been created.

This mechanism first generates a new string using JSON.stringify at which point it is no longer tied to the source object. It then generates a completely new object from that string using JSON.parse.

Note

Functions are not preserved if you use the JSON method, as functions can not be serialized in any standard JSON format.

James Hay
  • 7,115
  • 6
  • 33
  • 57
  • But if I delete `a['tag']`, a will be {} and b will be `{'tag': {'name': 'gavin'}}`. Why b doesn't change to `{}`? – gavinSong Dec 19 '17 at 07:49
  • 1
    @gavingSong because the `delete` operand acts on the inner `{'name': 'gavin'}` object, which both parents do point to. – Kaiido Dec 19 '17 at 07:51
  • `delete` is a confusing word for many in JavaScript, because it does not really DELETE anything, all delete ever does is remove a reference from an object. Removal of the actual data from memory only occurs in the garbage collection phase and can not be controlled in code. If you do `delete a` you will notice that `a` is still there, since you cannot control deletion of actual data. `delete a['tag']` just says "Remove the reference to that single copy on this object only". – James Hay Dec 19 '17 at 07:59
  • @gavinSong You may want to look a little more in to object references vs object values. There's many resources out there which describe and explain it, probably better than I can. It is not a concept tied to a particular language, it is at a lower level related to how data is stored in memory. – James Hay Dec 19 '17 at 08:06