Let's now create a similar example
to what we had in the last video,
just so that we can actually see this happening in practice.
And just like in a last video,
let's start by mutating a primitive value.
So, let's now use the last name
and I'm gonna set it here to Williams.
And so that's the initial value of this last name.
Then, let's also copy it,
and so that's because let's say
that this person gets married
and decides to change their last name.
However, we still want to remember the old name.
And so, we say oldLastName
is equal to the current last name,
at this position of the code.
But then we mutate actually the last name to Davis.
So, basically when this person gets married,
they then change their last name to Davis.
And of course, if we now check out both of them.
So, first the last name and then the old last name,
then we see that they are in fact different.
So, Davis is the new last name.
So, the one that we mutated here,
and Williams is this old last name
that was copied right here in this first line of code,
or actually the second line of code.
So, here, everything works
as we would expect in an intuitive way.
Now, remember, that it works this way
because each primitive value will simply be saved
into its own piece of memory in the stack, okay?
But now, let's do the same thing with an object,
which as we already know is a reference value
because it is gonna be stored in the heap,
and the stack then just keeps a reference
to the memory position
at which the object is stored in the heap.
So, let's create an object called Jessica.
And so, here we will have a first name,
of Jessica,
and a last name
of Williams.
Let's also give her an age of 27
just to make it more complete.
And now, let's again say that Jessica will get married
and will therefore change her last name.
And so this time, let's create a new object
for the married Jessica.
So, marriedJessica,
and it should be equal
to Jessica.
So, we're copying the entire object here.
At least that's what it looks like but behind the scenes
we are actually just copying the reference,
which will then point to the same object, remember that?
And so now, as we change the last name on marriedJessica,
so, last name let's change that to Davis,
but as we already know,
this will not give us the result that we expect.
So, before marriage,
let's log this original Jessica object, so to say.
And then after marriage,
and then here, the marriedJessica object.
Giving it a save.
And now as we see,
we get before the marriage and after the marriage,
both the last name of Davis, all right?
So, the last name Davis is now also
in the original Jessica object
and not just in the one that we copied.
And now at this point, we already know why this happened.
So, just to remember that, it happened because
when we attempted to copy the original Jessica object,
it did in fact not create a new object in the heap.
So, this one again, is not a new object in the heap.
It's simply just another variable in the stack,
which holds the reference to the original object.
So, both of these two variables simply point
to exactly the same memory address in the heap.
And that's because in the stack,
they both hold the same memory address reference.
And so of course, it makes sense
that if we change a property on marriedJessica,
it will also change on Jessica itself.
So, again, because they are essentially
just two different names for the same thing.
Now, this is also the reason why we can change properties
in the marriedJessica object,
which was declared using a const here.
And const is supposed to be for constants.
So, for things that we cannot change.
However, what actually needs to be constant
is the value in the stack.
And in this deck, the value only holds the reference,
which we are not actually changing.
The only thing that we are changing is the underlying object
that is stored in the heap.
And that is okay to change,
that has nothing to do with const or let, all right?
That's only about the value in the stack,
but if we change something in the heap
that has nothing to do with const or let.
Now, what we can't do
is to assign a completely different object now
to marriedJessica.
So, for example, we could not say marriedJessica
is equal to this new empty object, so that will not work.
Because this new object
will be stored at a different position in memory,
and therefore the reference to that position in memory
will have to change here in this variable.
And therefore, that does not work.
Because that is in the stack and so, since it is a constant,
we cannot change that value in the stack.
So, we cannot change the value to a new memory address,
and therefore, this does not work.
If it was a let here,
then we could do this, what we have here.
But since it's a constant, again, it is not allowed.
So, as a conclusion, completely changing the object,
so, assigning a new object to it
is completely different than simply changing a property
as we did here.
Okay?
So, it's a fundamental difference.
So, please make sure to really understand this.
Anyway, what if we actually really wanted to copy the object
so that we could then change one of them
without changing the other?
So, let me show you a way in which we can do that.
Let's first add some comments here once more.
So, primitive types,
reference types,
and then,
let's now say,
copying
objects.
All right.
And actually let's create a new Jessica object,
let me in fact, copy it from here.
So, let's say, Jessica2.
And so now, if we really wanted to copy this object,
we could use a function called object.assign.
And what this function does
is to essentially merge two objects
and then return a new one.
So, we could do this,
object.assign.
And then again, we can use this function
to merge two objects.
And so, what we can do
is to simply merge an empty new object
with Jessica2.
And this will then create a completely new object
where all the properties are really copied.
All right?
So, the result of calling this function here
with these arguments will be a new object.
And so, let's store that in
JessicaCopy.
And now, let's say that JessicaCopy gets married.
So, the same situation as before
changing her last name to Davis.
And now, let's lock both to the console just like here.
So, Jessica2,
and then here it's
JessicaCopy.
So, let's see what happens now.
And so, it's these last two here from line 222 and 223,
right?
And so now indeed,
we can preserve the original last name Williams
after we change the last name on this other object here.
So, this JessicaCopy.
And what this means is that this object now
is indeed a real copy of the original.
So, all the properties were essentially copied
from one object to the other.
And so, behind the scenes, what that means
is that a new object was in fact created in the heap
and JessicaCopy is now pointing to that object.
So, it has a reference to that new object.
However, there is still a problem
because using this technique of object.assign
only works on the first level.
Or in other words, if we have an object inside the object,
then this inner object will actually still be the same.
So, it will still point to the same place in memory.
And that's why we say that this object.assign
only creates a shallow copy
and not a deep clone which is what we would like to have.
So, again, a shallow copy will only copy the properties
in the first level while a deep clone would copy everything.
And to illustrate this,
let me actually show you what I mean.
And so, let's add an array here in this original object.
So, an array of family.
And this is gonna work as an example
because an array is really just an object behind the scenes.
So, here I can prove my point to you with this.
So, we have now an array called family, okay?
And so, you'll see that now this array here
is indeed in both objects.
So, okay.
So far so good.
But now, let's actually change that array in JessicaCopy.
So, in this one,
because as Jessica married, she also increased her family.
And so, let's now add some more family members there.
So, JessicaCopy.family,
and then let's use the push method to add a new element
to the end of the array.
Let's add Mary and let's also add another one.
So, family.push,
and let's add John.
So, remember here we are manipulating the copied object,
which is actually a copy,
but now here we are manipulating an object
that's within the object.
So, that's this object.
And so, now as we save this,
it still looks the same,
but that's because we need to log, of course,
after this operation happens.
And so now, we see that both the objects
now have a family with four members.
Let's open this a little bit, give it some more space.
And so indeed, now before the marriage and after marriage,
the family has all these four members, all right?
So, the last name was of course,
as we already saw preserved.
So, the name before the marriage is Williams,
and that was preserved because that's in the first level
and object.assign here took care of copying that property.
And so that was not changed as we changed the last name
in the copy.
However, the family object is a deeply nested object.
And so therefore, object.assign did not really,
behind the scenes, copy it to the new object.
All right?
So in essence, both the objects, Jessica2 and JessicaCopy
have a property called family,
which points at the same object in the memory heap,
and that object is, of course, this array.
And so, when we change the array in one of them,
it's also gonna be changed in the other one.
Now, a deep clone is what we would need here,
but it is not easy to achieve,
and it would actually be beyond the scope of this video
to learn how to create a deep clone.
Usually, we do something like this
which is like really complex
using an external library, for example, like Lo-Dash,
and this library has a ton of helpful tools
and one of them is for deep cloning.
And actually, we will do that in a later section
just so you see how we can include an external library
to do this kind of stuff.
All right.
And with this, we actually finish this section
about how JavaScript works behind the scenes.
And it was a long one with so many things
and so many new concepts to learn.
And many of them were hard and probably confusing,
but that's not a problem.
Though it's always a part of learning,
and even if you did not understand 100% of everything,
you're still good to move on in the course
to the next section now.
But just reaching the end of this section
already gave you a big advantage over many other developers,
which have no idea about many of the things
that I showed you here in this section.
So, congratulations for sticking with it to the end
and for learning all of this valuable knowledge.
And now, I see you in the next section.