자바스크립트는 비교적 메모리에 대한 부담이 없던 시기에 만들어졌기 때문에 이전에 만들어진 C/C++ 과는 다른 데이터 구조를 가지고 있다.
예를들어 숫자도 long, int 등으로 나누어 메모리 크기를 달리한 C/C++과 달리 JavaScript에서는 number라는 타입 하나로 통일하였다.
또한 가변적인 크기를 가진 문자열의 크기도 제한하지 않았다.
위와 같은 구조를 가진 자바스크립트에서는 다음과 같이 메모리를 관리한다.
1. 변수가 선언 될 시, 변수에 바로 값을 저장하지 않고 값의 참조 주소를 저장한다.
데이터가 바로 입력될 시, 데이터의 크기만큼의 메모리를 소모하게되며, 반복되는 데이터 중복되기 때문에 역시 효율적으로 관리할 수 없다. 따라서 데이터를 저장하는 메모리 영역을 따로 관리하여, 해당 영역에서 원하는 데이터의 주소를 참조하는 방식으로 관리한다.
2. 데이터 영역 메모리에서 값은 변경되지 않고 새로 생성된다.
문자열을 예로 들었을 때, 가변적인 문자열은 해당 값이 삭제되거나 변경되면 해당 영역만큼 없어지거나 크기가 변경된다. 그렇다면 해당 영역 뒤의 메모리 주소에 있는 값들은 앞쪽에 변경에 따라서 새로운 주소로 이동해야할 비효율적인 동작이 이루어진다. 따라서 값이 변경 때 기존에 영역을 수정하지 않고, 새로운 메모리를 할당하며 값을 저장한다. 이러한 점을 보았을 때 데이터 영역에 저장되는 값은 불변하다고 볼 수 있다. (변하지 않고 새로 생성되는 이유에서)
3. 사용하지 않는 데이터 영역의 값은 가비지 컬렉터에서 수집하여, 삭제한다.
사용되지 않는 값은 가비지 컬렉터에서 수집하여 삭제한다. 예를들어 a라는 값을 가진 데이터 영역이 사용되다가 변수의 값 재할당으로 인해 사용되지 않는 경우가 있다. 이럴 때 가비지 컬렉터는 a라는 값이 사용되지 않다는 것을 판단하여 해당 영역에 다른 데이터가 올 수 있도록 해준다. (효율적인 관리가 가능하다)
자바스크립트의 데이터 타입은 크게 두가지로 구분된다.
원시값
종류: Number, String, Boolean, undefined, null, Symbol
설명: 데이터 주소를 참조, 불변성
참조값
종류: Object, Array, Function 등
설명: 데이터 주소fmf 참조, 해당 데이터 주소값은 참조값에 대한 새로운 메모리 영역의 주소를 참조, 가변성
원시값과 참조값은 데이터 주소를 참조한다는 공통점이 있다만, 원시값은 해당 데이터를 참조하는 것이 끝이지만 참조값은 데이터 참조를 통해 새로운 메모리 주소를 가진 영역을 재 참조한다.
만약 원시값에 값의 재할당이 이루어졌다고 가정하면, 데이터 영역의 메모리에서 새로운 값을 생성하고 기존 값을 가비지 컬렉터에 수집되도록 한다. 따라서 값의 변경이 이루어지지 않기 때문에 불변한 값이라고 할 수 있다.
참조값에서 내부 프로퍼티 값의 재할당이 이루어졌을 때는 1차로 참조하고 있는 데이터에서 참조하고 있는 참조주소는 변하지 않는다. 해당 데이터에서 참조하고 있는 참조주소에서는 프로퍼티가 데이터 영역의 값을 참조하고 있으며, 해당 프로퍼티의 데이터 영역의 참조 주소만 변경되게 된다. 결론적으로 1차로 참조하고 있는 참조주소는 같고 2차로 메모리 영역 내에서 프로퍼티가 참조하고 있는 데이터 영역의 참조 주소가 변경 된다. 따라서 가변한 값이라고 할 수 있다.
참조 값의 특성 상 복사가 일어날 때 해당 참조값의 프로퍼티 영역의 메모리 영역 주소를 참조하게 되는 문제점을 야기한다.
var obj1 = {c:10, d:'ddd'};
var obj2 = obj1;
var obj2.c = 20; // obj1과 obj2는 같은 주소를 참조하기 때문에 변경 공유
console.log(obj1.c, obj2.c) // 20, 20
위와 같은 경우는 개발자가 의도한 바가 아니다.
따라서 참조값에 불변성을 부여하기 위해 여러가지 방법을 고려하였다.
다음 방식에서 기본적으로 고려하는 것은 내부 프로퍼티에도 참조값이 있다는 것이다.
1. 얕은 복사
해당 방식은 참조값의 바로 자식 프로퍼티만 불변한 객체로 만들어주며 더 깊은 프로퍼티들은 불변성을 부여하지 못하는 문제가 있다.
var copyObject = function (target) {
var result = {};
for (var prop in target){
result[prop] = target[prop];
}
return result;
}
2. 깊은 복사
참조값의 바로 자식 프로퍼티 보다 더 깊게 형성되어있는 참조값도 불변객체를 만드는 방법으로 재귀적인 방식으로 모든 값을 확인한다.
var copyObjectDeep = function(target) {
var result = {};
if (typeof target === 'object' && target !== null) {
for (var prop in target) {
result[prop] = copyObjectDeep(target[prop]);;
}
}
else {
result = target;
}
return result;
}
3. JSON parsing
간단한 방식으로 JSON으로 문자열화 하였다가 다시 객체로 변환하는 방식, 해당방식은 getter/setter, __proto__ 와 같은 JSON으로 변경할 수 없는 프로퍼티는 무시하기 때문에 단순한 데이터를 다룰 때 좋다.
var copyObjectJSON = function (target) {
return JSON.parse(JSON.stringify(target));
};
'JavaScript Study' 카테고리의 다른 글
REST 아키텍처, RESTful API란 (1) | 2024.06.03 |
---|---|
JavaScript - #23. Strict Mode (0) | 2023.09.22 |
JavaScript - #21. 일급객체 (0) | 2023.09.18 |
JavaScript - #20. 생성자 함수에 의한 객체생성 (0) | 2023.09.13 |
JavaScript - #19. 프로퍼티 어트리뷰트 (0) | 2023.09.11 |