Value types or referene types is making it difficult for you to approach JavaScript. Or you already have a lot of experience and want to revisit this concept. This is an article to help you strengthen your knowledge about value types and reference types.
The goal of
the article is to help you understand the concept of value types and reference
types. From this, it is possible to distinguish these two concepts. Makes
coding or bug fixing easier.
Table of
contents:
· What is value types?
· What is reference types?
· Work with reference type
Let’s go to the content!
What is
value types?
First, let's
go over the concept of primative variables in JavaScript. In JavaScript has
basic data types. These data types are not Object (Array, Object) and have no
method.
There are seven
types of premative variables in JavaScript including: string, number,
undefined, symbol (ES6), bigint, boolean, null.
All
primative variables are immuable. It's mean can't be alter. Variables can be
assigned a new value, but no method of array or object can be used to change
the value.
Once you
have a solid understanding of primative variables in JavaScript. We can say
that primative variables are value types. Let's see the example to understand
better:
let a = 0
let b = a
a = 5
// What is a ?
// What is b ?
Would you
answer this question something like this?
First, we
initialize let a = 0. The variable a is having the value = 0. We
initialize let b = a. Now the variable b is having the value = a
and = 0. Then we assign it again to a = 5. And finally, a has the
value = 5 and b has the value = 0. Let's see if the result is like we explain.
let a = 0
let b = a
a = 5
console.log(a) // => 5
console.log(b) // => 0
Completely
correct with the above explanation.
Let's go deeper to understand how the engine in JavaScript works. First, when creating variable a, the system will allocate a separate memory area, name this memory area 0x00a. And the memory area 0x00a is storing the value 0. Then, we initialize the variable b = a, the system will continue to allocate another memory area. This memory area let's name it 0x00b and is storing the value is equal to the variable a = 0. Then we change the value of the variable a to 5. It means that the memory area 0x00a is storing the value as 5. Let's see the table below to summarize.
Variables |
Address |
Value |
a |
0x00a |
0 |
b |
0x00b |
0 |
a = 5 |
0x00a |
5 |
When we
change the value of variable a, then the value of memory 0x00a will
change. Memory 0x00b is not affected. So, when we print the result,
changing the variable a does not affect the variable b at all.
To make this more clear, let's see the example below:
let a = 0
let b = 0
console.log(a === b) // => true
let c = 0
let d = c
console.log(c === d) // => true
Through the
above example we can say, the variables have the same value even though they
are in different memory areas. And the engine in JavaScript uses === to compare
values and data types, then we have returned results that are completely
correct.
What is
reference types?
We can see
that all primative variables are value types. So that means the remaining
objects will be reference types. We have Object, Array, Function as reference
types.
Let's go
through a similar example above to see the difference:
let david = {
name: 'David'
}
let oliver = david
oliver.name = 'Oliver'
console.log(oliver.name) // => What is the result ?
console.log(david.name) // => What is the result ?
Let's
analyze to predict the outcome. We instantiate a let david object and
assign it a value of “name: 'David'”. We continue to instantiate a let
oliver object and assign it to the value of the david object which
means that the oliver object has the value name: 'David'. We
change the name value of the oliver object to 'Oliver'. Finally, the
result will return oliver.name === 'Oliver' and david.name ===
'David'. Let's see the results:
let david = {
name: 'David'
}
let oliver = david
oliver.name = 'Oliver'
console.log(oliver.name) // => Oliver
console.log(david.name) // => Oliver
Wow! Unexpectedly,
things did not go as we expected. Let's analyze to find out what's going on.
When we
first create the david object, the system also creates a memory area and
stores the value { name: 'David' }. Name this memory address 0x00david.
The variable david when created has the value of the name of the memory
address 0x00david, not the value { name: 'David' }. Then we
create the oliver object equal to david. Now the value of the oliver
variable will be equal to the address of the memory area which is 0x00david
and not {name: 'David'} at all. That means david and oliver are pointing to
the same memory area. When we change the value of oliver.name, it also
means that we are changing the common value of the two objects, so when we log
the results, we see that both have the same result. Check out the table below
for a summary.
Variables |
Address |
Values of address |
Values of variables |
David |
0x00david |
{name: ‘david’} |
0x00david |
Oliver |
0x00david |
{name: ‘david’} |
0x00david |
Oliver.name = ‘oliver’ |
0x00david |
{name: ‘oliver’} |
0x00david |
We can come
to the conclusion that the object when created will be assigned the storage
address of the value, not the value.
At the same
time, creating a new object will create a new memory area. Let's see the
example below:
let a = {}
let b = {}
console.log(a === b) // => false
As discussed
above, let a and let b only store the address of the created object. Although
the two values are the same. But when comparing let a and let b we are only
comparing two addresses, not comparing two values. Check out the summary below:
Variables |
Address |
Values of address |
Values |
a |
0x00a |
{ } |
0x00a |
b |
0x00b |
{ } |
0x00b |
We see that
the values in the two memory areas are the same. But the value that the two
variables are storing is the address where the values are stored.
And through
the example above we can see how many objects are created, how much memory is
created. No matter how many times objects are nested. Let's see an example to
understand better:
let parent = {
dad: 'Dad',
mom: 'Mom',
child: {
name: 'child'
}
}
let otherChild = parent.child
otherChild.name = 'otherChild'
console.log(parent)
// => Object { dad: "Dad", mom: "Mom",
child: { name: 'otherChild } }
console.log(otherChild)
// => Object { name: "otherChild" }
We only
change the value in the memory area, not the memory address. So when called
from any source will only change the value there.
Work with
reference types
Do you think
that now you want to change the value in that memory area but not affect other
objects that are refering to that memory area, what will happen? I have the
following 2 ways for 2 cases, you can refer and apply,
Case 1: For
a single-level object
const user = {
name: 'User name',
pasword: 'Password'
}
const otherUser = { ...user }
otherUser.name = 'Another user name'
console.log(user.name) // => User name
console.log(otherUser.name) // => Another user name
This means
you are creating a new memory area for the otherUser variable. So, when you
change the value of otherUser, the user is not affected. This is how you do it
when you want to copy an object without changing the original object.
Case 2: For
a muti-level object
The above
method is only applicable to single-layer Objects. Let's go through the
limitation in the first way:
const user = {
name: 'User name',
pasword: 'Password',
account: {
balance: '$1000'
}
}
cosnt otherUser = { ...user }
otherUser.account.balance = '$2000'
console.log(user.account.balance)
// => $2000
console.log(otherUser.account.balance) // => $2000
As you have
seen, you will change the value of both objects. This is something you don't want
at all. Let's go through the second way to apply in this case.
const user = {
name: 'User name',
pasword: 'Password',
account: {
balance: '$1000'
}
}
const otherUser = JSON.parse(JSON.stringify(user))
otherUser.account.balance = '$2000'
console.log(user.account.balance)
// => $1000
console.log(otherUser.account.balance)
// => $2000
Work! The
main point in both ways above is that the newly created object creates a new
memory pool. And the fact that you change the value in separate memory areas
can't affect each other.
Another
case:
You will
wonder that in the above cases, I use const to initialize but why can I change
the values. Is the theory of const in this case wrong?
The theory
of const is always true in this case. Because I only change the value in the
memory area, not the address that the variable is pointing to. So, I can change
the values inside that memory.
Conclusion
You have
come across the concept of premative variables as value types. The rest are
reference types. Value types is a variable that when created will store the
value. And when the reference types is created, the variable stores the address
to that memory area. How to deal with reference types.
If you have
any comments, feel free to comment below. Thank you for joining with me. Have a
good day!
0 Nhận xét