Now that's it for functions.
Now, I, again,
wanna look behind the scenes of JavaScript,
and specifically,
I wanna explain why this kind of code worked.
This is some code which we had in an earlier course section
and there we have an array and we stored that array in a
constant, and then I push a new value into that array.
And hence,
we clearly changed the array because we add a new value.
Nonetheless, that code worked even though for a constant,
you shouldn't be able to change
the value stored in the constant.
Now back then I explained that for arrays and also objects,
that's kind of a special scenario and you can change
the object and array itself.
As long as you don't assign a new value to the constant
itself with the equal sign.
I now wanna explore this again and explain in greater
detail why it works like this.
And for this, I'll add a new file,
which I will name
behind-the-scenes.js,
and in there, I'll again,
add an array and it's an array of hobbies let's say.
And in there I have sports and cooking.
That's my array here.
Now I'll do what I did before.
And I'll push a new value into that array, for example,
reading.
And if we then console log, hobbies thereafter,
you will see that if we run this behind the scenes JS file
with node,
we get this updated array and we don't get any error.
Now that's what we saw before,
but why exactly does this work?
Why does const only care about whether we try to assign a
new value with the equal sign,
instead of just ensuring in general that the underlying
value doesn't change?
Well in the end,
because that's how JavaScript works.
But there also is a reason for that.
Arrays and objects,
and actually just like functions,
arrays are objects under the hood,
just special kinds of objects you could say.
So therefore, objects,
which include arrays are stored in a special kind of memory
by JavaScript.
Now we could dive super deep into that,
but it doesn't matter too much right now.
It's just important to keep in mind that objects
and therefore all the arrays are stored
in a special kind of memory.
And for example,
numbers or any other kind of value like strings or
booleans are stored in a different kind of memory
because numbers, strings and booleans,
and a couple of other values are so-called primitive values.
So we have our primitive values, which are numbers,
strings and booleans and all couple of other values that
don't matter too much right now, for example, undefined,
which we saw before is also such a primitive value.
And then we also have so-called the reference values,
or reference value types,
and that would be objects and since arrays and also
functions are objects.
Those values are also reference values and reference values
are stored in a different kind of computer memory
than primitive values are.
The reason for that is simply that
objects tend to be a bit more complex after all they can
contain multiple values.
Multiple key value pairs, maybe even some methods.
The primitive values are really primitive.
They are simple, it's a single number or a single string,
even if it's a longer string, it's still pretty simple.
And hence primitive values are stored in a more basic kind
of computer memory. Objects are stored in a more advanced
kind of computer memory. You could say.
Now, because objects can and tend to be more
complex.
They are stored such that unnecessary copies of objects
are avoided simply because a simple number doesn't take up a
lot of space and memory, a complex object does.
So we wanna avoid that we are unnecessarily copying
objects around.
Therefore,
and that's now the part where this push thing combined with
a constant will make more sense. Therefore,
when you create an array or an object and you store it in a
variable or a constant, what you actually stored
there is not the value. So the object or array itself,
but a pointer to that object or array in memory.
So the address of that object in memory.
You don't see that here in the code.
It looks like the array itself is stored here,
but that's actually not what happens. Instead.
A pointer to the array is stored.
If you store a number on the array or a string,
the value itself is stored in the variable or a constant.
Now, since that's the case,
when you push a new value to that array,
this value is added to the existing array in memory,
but the pointer doesn't change, the address of that
array in memory doesn't change.
So the address of the array doesn't change,
and that's why we can do that on a constant,
because the constant stored the address,
and the address didn't change.
So when you call push to add a new value to an array,
that actually tells JavaScript to look up that address,
go to the actual array in memory and add that value there.
The value stored in the constant did not change because that
value is the address not the array itself.
That's why you can do that. On the other hand,
if you would try to set hobbies equal to a new array where
you have coding and sleeping or something like that,
that would not be allowed
because here we create a new array,
which receives its own new place in memory and which
therefore receives and you address.
And we would therefore try to store and you address and this
constant,
which is not allowed because it overrides the old value of
the constant, which was the old address.
So that's why we can push into an array that's stored
in a constant because of that reference,
whereas there's this primitive value thing.
Now I do have a complete guide on JavaScript,
which has 55 hours on its own,
where I dive way deeper into JavaScript and where I also
explain this concept in greater detail.
We don't need all these details right now,
but it's hopefully still clear that objects are simply
stored differently in memories,
than primitive values like numbers are
and that because of that,
and because we only store the address of the object in the
constant, instead of the object itself,
we can manipulate the underlying object or array without
manipulating the constant value itself.
Because the constant value is only that address.
This primitive words reference value
of thing also has a couple of important
implications.
Let's say we have an object now person,
which has an age value of 32.
Of course, that's a simple object.
It's almost only a number,
but still it is a object and we could have more properties
and methods in there.
I'm just trying to keep this simple here.
So let's say we have this person object with potentially
more properties, and then we have a function, not a method,
but a standalone function here,
which maybe gives us the number of adult years.
This person has. So I'll name us get adult years.
And I get a person here as a parameter,
or let's just name it P here to avoid any confusion with the
same name being used.
And what I do in here is I want to get my adult years
by setting p.age,
because I expect P to be an object
of that shape,
equal to p.age minus 18.
And hence,
we can also use this shortcut and write it like this.
And then I return P age,
which is now just a number of years above 18.
Since we deduct 18 from that age,
if we now console log get adult years and we pass the person
object that we created here into that,
we should get 14 as a result.
So if we now execute this file, indeed I do get 14 here,
but we have a side effect here.
Watch what happens if I also console log person after
calling get adult years.
If I now execute that file, we get 14 as before.
But when I output the person here,
I get age set equal to 14. And you might say sure,
because that's what we did here. But on the other hand,
we did that on the person we received as a parameter inside
of the function, not on this person,
this person should still have age of 32 because it's that
person, which I'm outputting here.
It's only the parameter value which we had inside of the
function that changed.
But why did this now also affect this person?
Because objects are reference values.
Keep in mind,
what's stored in person in this constant here is just the
address of that object just as we simply stored the address
of this array and hobbies before.
Now, since that's the case,
what we actually pass to get adult years when we call it
with person is the address.
So P instead of get adult years is just the address of the
object.
Now, when we use the dot notation here, we can,
of course do that.
And JavaScript will look up the object for that address for
us then, and give us the age property off that object.
But when I then changed his age off this object by deducting
18 from it,
I do not just change the person object in this function,
but this person object as well,
because it's the same object.
There is only one object because we're just working with the
address here.
I get the address of this person when I called get adult
years,
hence I'm operating on the exact person object in memory
when I deduct 18 year.
And that's why when I console log person there,
after we have the updated age here in the terminal,
because it's one and the same object,
and that's not a bug or an unintended side effect,
but instead that's a behavior, which we have on purpose,
because as mentioned,
objects can become very complex and therefore they are
managed such that unnecessary copies are avoided.
Hence,
the address is pass around instead of new copies of that
object,
therefore operations like this can have the unintended side
effect of changing the original object in places where we
don't wanna do that.
Now, how can we avoid this unintended side effect here then?
Well,
one work around would be that we don't update
the person age here, but instead,
if we just want to derive the adult years,
instead of doing it like this,
we can return p.age minus 18.
It's minus 18, not minus equal. With this line of code,
we are not setting a new value for person age. Instead,
we are just arriving a value based on the existing age value
minus 18,
but that derived value does not override the age in person,
but instead is simply returned as a value by get adult
years.
If we do that and we execute this file, we now have 14 here,
but we didn't change the original object.
That would be a better way of doing it.
If you have a function that for some reason needs to
manipulate the object itself, though.
So let's say we wanna go back to that original code.
Then you can still work around changing the original object
by passing in a copy of person into get adult years.
And to do that.
You just need to create a new object by again,
using the curly braces here,
where you either add all the properties manually.
So where you add an age property and set this equal to
person dot age with that,
we are creating a brand new object,
a brand new place in memory with a brand new separate
address.
And we just populate age based on the values stored in
persons age. But that is then a number which we use here.
So with that,
we're not pointing at the old person object
as a whole in this place.
And therefore, if we now would save that and run this code,
it would work without issues.
And the original object wasn't changed,
but creating objects like this,
and manually adding all the properties
which the original object has into this new object can be
very cumbersome.
That's why we can use another operator here,
which we already saw before. Between those curly braces,
we can use the free dot operator, the spread operator,
and spread our person object into this new object.
Now,
we haven't seen this usage of the spread operator before,
but the spread operator can indeed be used on arrays
or on objects and on arrays.
As you saw before. It pulls out all the individual values to
create a list of values.
When used on objects,
it pulls out all the key value pairs off the object and
gives you a list of those key value pairs.
And if you do that between curly braces,
you create a new object,
which gets a list of all the key value pairs of another
object.
And hence you get a new object in memory with a new address
with all the old key value pairs of another object.
That's a way of copying an object, of creating a brand new
object of the same shape
in a new place in memory and therefore that's
another way of working around that issue in quotes,
because it's not really an issue.
It's an intended feature
of having reference values for objects.
If we now execute this file here again,
we have the unchanged original object whilst also getting
the correct adult years.
With that code that changes the object.
It does not change the original object anymore because we
create a brand new object based on the original object here
in this line, 21 now.
And again,
that is certainly a bit more advanced and can be challenging
to grasp or to see why that's helpful.
But we are going to write more and more advanced code over
the next sections. And as a web developer in general,
you will be confronted with more advanced code like this,
and therefore you should be aware of this primitive versus
reference value thing.
And you also should know how you can create copies and work
around that reference value limitation,
which is sometimes face to avoid
unintended side effects as we had them at the
beginning, when we changed the original object,
because you very often
wanna avoid doing something like that.