const num = 1
const newNum = 1
console.log(num === newNum) // true
const obj = { num: 1 }
const newObj = { num: 1 }
console.log(obj === newObj) // false
num
과 newNum
은 같고, obj
와 newObj
는 다르다.
그 이유를 우리는 불변타입의 값을 변경하면 참조가 변경되고,
참조타입의 값을 변경하면 참조가 변경되지 않는다고 외우고 있다.
그런데 왜 그런걸까?
각 변수에 저장된 것은 값이 아닌 메모리 위치이기 때문.
메모리 위치가 같다면 같은 값이고, 메모리 위치가 다르다면 다른 값이다.
간단한 예제로 메모리 할당과정을 알아보자.
원시 타입(number, string, boolean)은 불변(immutable)이므로,
같은 값을 가진 변수를 선언할 때 새로운 메모리를 할당하지 않고 기존 값을 재사용한다.
따라서 값을 변경해도 값은 바뀌지 않고 참조 자체가 변경된다.
const num = 1;
const newnum = 1;
let num2 = 1;
num, newnum, num2은 모두 같은 값과 참조를 가지고 있다.
메모리 할당 과정을 따라가며 참조를 확인해보자.
숫자 1이 메모리에 저장되고, num은 해당 주소(0x1000
)를 참조한다
메모리 주소 | 값 | |
---|---|---|
0x1000 | 1 | 👈 num |
원시타입의 값은 불변하다.
값 1
이 이미 메모리에 저장되어 있으므로 새로운 공간을 할당하지 않고 기존 주소(0x1000
)를 참조한다
메모리 주소 | 값 | |
---|---|---|
0x1000 | 1 | 👈 num, newnum |
num2도 값 1을 가지므로 새로운 공간을 할당하지 않고 같은 메모리 주소(0x1000
)를 참조한다.
메모리 주소 | 값 | |
---|---|---|
0x1000 | 1 | 👈 num, newnum, num2 |
참조가 모두 동일하다.
그런데 만약 여기서 num2의 값을 바꾼다면 어떻게 될까?
num2 = 2
원시 타입은 불변(immutable)이므로, 기존 값을 수정하는 것이 아니라 새로운 메모리 공간을 할당한다 !
2가 새로운 주소(0x1001
)에 저장되고, num2는 이제 0x1001
을 참조하게 됨.
메모리 주소 | 값 | |
---|---|---|
0x1000 | 1 | 👈 num, newnum (참조 유지) |
0x1001 | 2 | 👈 num2 (새로운 메모리 공간 할당) |
num과 newnum의 참조는 동일하지만,
새로운 공간에 할당된 num2의 참조는 변경되었다.
Object,Array,Function 등의 참조 타입은 가변(mutable) 이다.
참조 타입은 값이 아닌 주소(참조)를 저장하며, 변수 간 복사가 이루어질 때도 값이 아닌 참조가 복사된다.
const obj1 = { value: 1 };
const obj2 = obj1;
let obj3 = { value: 1 };
객체 {value: 1}
은 새로운 메모리 공간(예: 0x2000
)에 저장되고,
obj1는 해당 객체의 주소(0x2000
)를 참조한다.
메모리 주소 | 값 | |
---|---|---|
0x2000 | {value: 1} | 👈 obj1 |
obj2는 obj1이 가지고 있는 주소값(0x2000
)을 그대로 복사한다.
= 새로운 객체가 생성되지 않고 같은 객체를 참조한다
메모리 주소 | 값 | |
---|---|---|
0x2000 | {value: 1} | 👈 obj1, obj2 |
obj3는 { value: 1 }이라는 새로운 객체를 생성하고, 다른 메모리 주소(예: 0x2001
)를 참조한다.
= obj3는 obj1, obj2와 완전히 다른 객체를 참조한다
메모리 주소 | 값 | |
---|---|---|
0x2000 | {value: 1} | 👈 obj1, obj2 |
0x2001 | {value: 1} | 👈 obj3 |
여기서 value 값이 변경된다면?
obj2.value = 2;
obj1과 obj2는 같은 객체를 참조하고 있으므로, obj1.value도 함께 변경된다.
메모리 주소 | 값 | |
---|---|---|
0x2000 | {value: 2} | 👈 obj1, obj2 |
0x2001 | {value: 1} | 👈 obj3 |
let으로 선언했던 obj3에 새로운 값을 할당해보자.
obj3 = { value: 3 };
메모리 주소 | 값 | |
---|---|---|
0x2000 | {value: 2} | 👈 obj1, obj2 |
0x2001 | {value: 1} | 참조되지 않음. GC가 수거해갈 수 있음 |
0x2002 | {value: 3} | 👈 obj3 |
obj3에 새로운 객체 {value: 3}
을 할당하면 새로운 주소(예: 0x2002
)를 참조하게 된다.
원시타입은 값만 복사해서 새로운 메모리 공간이 할당된다.
let num1 = 1
메모리 위치 | 값 | |
---|---|---|
0x1234 | 1 | 👈 num1 |
이때 num2에 num1을 저장하게 되면?
let num1 = 1
let num2 = num1
메모리 위치 | 값 | |
---|---|---|
0x1234 | 1 | 👈 num1 |
0x1235 | 1 | 👈 num2 |
num1의 값을 복사해서 새로운 메모리 위치에 저장한다.
num1의 값을 가져와 num2에 넣은 것을 알 수 있다.
이 둘은 서로 영향이 있을까?
그렇다면 이제 num2의 값을 변경해보자.
num2 = 2
메모리 위치 | 값 | |
---|---|---|
0x1234 | 1 | 👈 num1 |
0x1235 | 2 | 👈 num2 |
위치를 저장하지 않고 값만 가져와 새로운 위치에 저장했기 때문에,
num2를 변경해도 num1에는 아무런 영향이 없다.
참조타입(객체, 배열 등)은 값을 복사하는 것이 아니라 주소를 복사한다. 따라서 복사된 두 변수는 같은 객체를 참조하게 된다.
let obj1 = { value: 1 }
메모리 위치 | 값 | |
---|---|---|
0x2000 | { value: 1 } | 👈 obj1 |
let obj2 = obj1
이때 obj2에 obj1을 저장하면
메모리 위치 | 값 | |
---|---|---|
0x2000 | {value: 1} | 👈 obj1, obj2 |
obj1의 주소를 obj2에 복사하여 두 변수는 동일한 객체를 참조하게 된다.
그럼 여기서 obj2의 값을 변경하면 어떻게 될까?
obj2.value = 2
메모리 위치 | 값 | |
---|---|---|
0x2000 | {value: 2} | 👈 obj1, obj2 |
obj2를 변경하면 obj1도 영향을 받는다 !
왜냐하면 두 변수는 같은 객체를 참조하고 있기 때문이다.
obj1 = { value: 3 }
이제 obj1에 새로운 객체를 할당하면, obj2는 여전히 기존 객체를 참조하고 있다.
메모리 위치 | 값 | |
---|---|---|
0x2001 | {value: 3} | 👈 obj1 |
0x2000 | {value: 2} | 👈 obj2 |
새로운 객체가 할당되었기 때문에 obj1은 새로운 메모리 주소를 참조하게 된다.