메모리와 값의 복사
# 원시 값과 객체가 메모리에 저장되는 방식
자바스크립트의 데이터 타입은 크게 원시 값과 객체로 구분할 수 있다. 원시 값은 메모리에 저장될 때 원시 값의 실제 값이 저장되고, 객체는 메모리에 저장될 때 객체의 참조 값(= 메모리의 주소)이 저장된다.
1. 원시 값이 메모리에 저장되는 과정
let age;
age = 20;
age = 21;
age라는 변수에 원시 값인 숫자를 할당하는 과정이다. age는 let으로 선언되고 나서 가장 처음으로 undefined가 할당된다. 그리고 20이라는 값이 할당되면, 새로운 메모리 공간에 20이라는 저장되고, age는 참조하던 메모리 공간 주소를 변경한다. 즉, 원래의 메모리 공간의 값이 바뀌는 것이 아니라, 20이라는 새로운 메모리 공간이 생기고 age는 그 새로운 메모리 공간을 참조하는 것이다. 원시 값은 불변이기 때문이다.
1.1 값에 의한 전달
원시 값이 복사되는 과정을 알아보자.
let score = 90;
let copy = score;
console.log(score); // 90
console.log(copy); // 90
score = 100;
console.log(score); // 100
console.log(copy); // 90
score라는 변수에 90이라는 값이 저장되었다. 그리고 copy 변수에 score를 할당했다. 이때 score의 값을 변경하면 copy의 값도 같이 변경될까? 답은 아니다. score의 값이 변경되어도 copy의 값에는 영향을 주지 못한다. 이 상황의 핵심은 '변수에 변수를 할당할 때 무엇이 전달되는지' 이다.
copy라는 변수에는 score의 값만 전달이 된다. score가 사용하는 메모리 공간과 copy가 사용하는 메모리 공간은 다른 곳이다. 즉, score의 90과 copy의 90은 다른 공간에 있는 값이다. 따라서, score의 90이 100으로 변경되어도 copy의 90은 아무런 영향을 받지 않는다.
2. 객체가 메모리에 저장되는 과정
원시 값을 가지는 변수는 메모리 공간에 저장된 원시 값 그 자체에 접근한다. 그러나 객체를 가지는 변수는 참조 값(= 메모리 공간 주소)을 거쳐서 객체에 접근한다.
const user = {
name: "kim",
age: 20
};
변수에는 객체의 내용이 저장된 메모리 공간의 주소가 저장되어 있다. 즉, 객체 속의 내용은 별도의 메모리 공간에 저장하고, 그 메모리 공간의 주소를 변수에 저장하는 것이다. 그리고 변수는 해당 주소를 참조하여 객체 값에 접근한다.
만약 객체도 원시 값처럼 내용을 변경할 때마다 메모리 공간을 새로 만든다면 매우 복잡해질 것이다. 객체는 크기가 제각각이고, 크기가 매우 클 수도 있고, 객체 안에 또 다른 객체가 있을 수도 있기 때문에 원시 값과 동일하게 처리한다면 굉장히 비효율적이게 된다. 따라서 메모리를 효율적으로 사용하기 위해서 참조 값이라는 방법으로 객체를 관리한다.
그러나 이런 방식에도 단점은 있다. 여러 개의 식별자가 하나의 객체를 공유할 수 있다는 것이다.
2.1 참조에 의한 전달
객체가 저장된 변수를 다른 변수에 할당하면, 원본의 참조 값(= 메모리 주소)가 복사된다.
const user = {
name: "kim",
age: 20
};
const copy = user;
객체를 값으로 가지는 변수는 다른 변수에 할당되면 참조 값이 복사된다. copy라는 변수에 user라는 변수를 할당하면, 객체의 참조 값이 복사된다. 이를 참조에 의한 전달이라고 한다. 그리고 이렇게 되면, 각 변수가 같은 메모리 주소를 참조하게 된다. 즉, 여러 개의 식별자가 하나의 객체를 공유하여 사용하게 되는 것이다. 따라서 user나 copy 중 어느 한 쪽에서 객체 값을 변경하게 되면 서로 영향을 받는다.
const user = {
name: "kim",
age: 20
};
const copy = user;
user.name = "lee";
console.log(user.name); // "lee"
console.log(copy.name); // "lee"
user.name의 값을 변경하였지만 copy.name의 값도 같이 변경되었다. 이처럼 객체를 값으로 가지는 변수는 서로 영향을 받는다.
# 객체의 얕은 복사(shallow copy), 깊은 복사(deep copy)
위에서 봤듯 객체의 값을 복사하면 객체의 참조 값이 복사되는데, 이를 얕은 복사(shallow copy)라고 한다. 그러나 얕은 복사로 복사된 객체는 서로 영향을 준다는 문제가 있다. 이 문제를 해결하기 위해 참조 값을 복사하는 것이 아니라 원시 값처럼 완전한 복사본을 만들 수도 있는데, 이를 깊은 복사(deep copy)라고 한다.
1. 얕은 복사
얕은 복사는 위에서 알아본 예시와 같다.
const user = {
name: "kim",
age: 20
};
const copy = user;
user.name = "lee";
console.log(user.name); // "lee"
console.log(copy.name); // "lee"
2. 깊은 복사
{...객체} 라는 표현을 이용하면 깊은 복사를 할 수 있다.
const user = {
name: "kim",
age: 20
};
const copy = {...user};
user.name = "lee";
console.log(user.name); // "lee"
console.log(copy.name); // "kim"
원시 값처럼 아예 새로운 복사본을 만들어 저장하는 것이기 때문에, 값을 추가하는 것도 가능하다.
const user = {
name: "kim",
age: 20
};
const copy = {...user, id: 123};
console.log(copy.id); // 123
user에는 id라는 프로퍼티가 없지만 copy에는 추가할 수 있다.
참고 자료
모던 자바스크립트 Deep Dive 11장
'FE > JavaScript' 카테고리의 다른 글
[JavaScript] 이벤트 (0) | 2022.11.05 |
---|---|
[JavaScript] for, forEach, map 차이 (0) | 2022.11.02 |
[JavaScript] 구조분해할당 (0) | 2022.10.26 |
[JavaScript] scope와 var, let, const (0) | 2022.10.25 |
[JavaScript] callback 함수 (0) | 2022.10.24 |