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.