[JS] 코어자바스크립트 - 데이터 타입
코어 자바스크립트를 꼼꼼하게 정독하고
복습하며 정리한 내용을 토대로 F-lab 멘토링을 받고있다.
이 글은 멘토링으로 알게된 것들을 다시한번 정리해보는 글이다.
데이터 타입 - 자바스크립트가 데이터를 처리하는 과정
배경지식 - 메모리와 데이터
C/C++, Java 등 정적타입 언어는 메모리 낭비 최소화를 위해 타입마다 할당되는 메모리 영역을 정해놓았다.
short 타입으로 정의한 정수형은 허용범위 크기 이상의 숫자를 입력하면 오류가 나기때문에, int나 long 타입으로 바꿔주는 작업이 필요하다. 이것은 용량이 부족하던 시절에는 불가피한 선택이었다고 한다.
하지만 메모리 용량이 월등이 커진 상황에서 등장한 자바스크립트는 타입에 상관없이 메모리를 좀 더 넉넉하게 확보할 수 있게 되었다. 덕분에 위와 같은 타입변환을 걱정해야하는 상황은 훨씬 줄어들었다.
자바스크립트에서 숫자의 경우 정수형과 부동소수형을 구분하지 않고 8바이트씩 할당하는데, 사실 현실에서 활용하는 왠만한 숫자의 범위는 8바이트 안에서 표현이 가능하다고 한다.
배경지식 - 식별자와 변수
혼용하기 쉬운 변수와 식별자의 개념을 구분할 필요가 있다.
변수는 변할 수 있는 데이터를 의미하고
식별자는 그 데이터를 식별하는데 사용하는 변수명을 의미한다.
데이터 타입의 종류 - 기본형과 참조형
데이터 타입은 기본형
과 참조형
으로 크게 두가지가 있다.
기본형
: 숫자, 문자, bloolean, null, undefined 등
참조형
: 객체, 배열, 함수 등
두 타입을 분리하는 기준은 불변성
에 있다.
그것을 이해하기 위해 자바스크립트에서 데이터가 할당되는 과정을 알아야한다.
변수의 선언과 데이터 할당
변수 선언
변수란 변경 가능한 데이터가 담길 수 있는 공간이다.
- 변수의 선언으로 컴퓨터는 메모리에서 비어있는 공간 하나를 확보한다.
- 이 공간의 이름을 변수명(식별자 이름)으로 지정한다.
이후에 이 변수에 접근하려면 변수명이 가지고 있는 데이터주소값을 찾아
데이터를 반환하게 된다.
데이터 할당
앞에 선언한 변수명의 공간에 데이터를 직접 저장하지 않는다.
데이터를 저장하기 위한 별도의 메모리 공간을 다시 확보해
데이터를 저장하고, 그 주소를 앞에 확보한 변수영역에 저장한다.
변수영역과 데이터영역을 분리해 저장하게 되는 것이다.
왜 분리해서 저장할까?
데이터 변환을 자유롭게 함과 동시에 메모리를 더욱 효율적으로 관리하기 위함이다.
문자열처럼 정해진 규격이 없는 데이터의 경우 필요한 메모리 용량이 가변적이다.
미리 확보한 공간 내에서만 데이터 변환을 할 수 있다면
바뀐 데이터 저장을 위해 확보했던 공간을 늘리는 작업이 선행되어야 한다.
하지만 데이터가 중간에 위치한 경우, 뒤에있는 데이터들을 옮기는 작업을 해야하므로 효율성이 떨어지게 된다.
- 자유로운 데이터 변환
문자열 abc에 def를 추가하고자 하는 경우
컴퓨터는 abcdef가 저장될 공간을 데이터영역에서 새로 만들어 저장하고
그 공간의 주소를 변수공간에 연결시킨다.
변수와 데이터를 별도의 공간에 저장하여 변화에 대응이 쉽도록 하는 것이다. - 효율적 메모리 관리
5라는 데이터를 갖는 500개의 변수가 필요한 상황이라면,
변수 공간은 500개가 생성되겠지만 그 변수 공간들이 5라는 데이터가 있는 공간의 주소를 동일하게 연결하면 되므로 중복데이터에 대한 처리 효율도 높일 수 있다.
이렇게 자바스크립트가 변수를 저장하는 방식을 통해 불변값과 가변값의 차이에 대해 생각해 볼 수 있다.
기본형 데이터 : 불변값
변수와 상수
데이터 할당이 이루어진 변수공간에 다른 데이터를 재할당 할 수 있는지에 따라 변수
와 상수
를 구분한다.
변수와 상수를 구분짓는 기준은 변수 영역이 된다.
불변성
반면 불변성
여부를 구분할때의 기준은 데이터영역이다.
데이터를 변경할때 메모리는 변수영역에서 값을 바로 바꾸지 않고,
데이터영역에 바꿀 값이 존재하는지 확인 후
해당 값이 없다면 새로 데이터 공간을 확보해 저장하고,
변수영역에는 바뀐 데이터영역의 주소를 연결시킨다.
따라서 한번 만든 값은 없어지지 않고,
변수의 데이터를 재할당하는 과정 역시 데이터영역을 새로 만드는 과정을 통해 이루어진다. 이것이 불변값의 성질이다.
한번 만들어진 값은 가바지컬렉팅을 당하지 않는 한 영원히 변하지 않는다.
가비지컬렉터 Garbage Collector
가비지컬렉터는 특정 시점이나 메모리 사용량 포화 시 자동으로 자바스크립트 엔진이 작동시킨다. 가비지컬렉터를 통해 데이터가 수거된 메모리는 다시 빈 공간이 된다.
참조형 데이터 : 가변값
참조형 데이터는 가변값
을 가진다고 말할 수 있다.
참조형 데이터인 객체나 배열 등은 그룹형 데이터 안에 또 다른 그룹형 데이터를 가질 수 있기 때문이다.
이런 것을 중첩 객체라고 한다.
참조형 데이터는 변수영역에 공간을 할당한 후, 데이터영역에서 그룹형 데이터 (여러 속성값)의 저장을 위해 다시 한번 별도의 변수영역을 마련한다.
별도로 마련한 것은 변수영역이지 데이터영역이 아니다. 데이터는 기존의 데이터영역을 그대로 활용한다.
참조형 데이터는 내부 속성 값이 바뀌더라도 기본 변수영역의 데이터주소가 변하는 것이 아니라, 추가로 할당된 또다른 변수영역에서 주소값이 변한다.
그런데 만약 중첩객체에서 속성값 중 하나를 기본데이터 값으로 재할당한다면, 따로 마련된 변수영역은 더이상 자신을 참조하는 변수가 없게된다. 이것을 참조카운트라고 하는데, 참조카운트가 0이 된 메모리는 가비지컬렉터의 수거 대상이 된다.
변수 복사 - 기본형과 참조형 비교
기본형 데이터의 복사
- a변수를 생성하고, 데이터 10을 저장해 주소값을 연결시킨다.
- b변수를 생성해 변수 a를 할당한다. 그러면 a와 b는 같은 데이터 주소값을 갖게 된다.
참조형 데이터의 복사
- obj1 변수를 생성한다. 속성값들이 담길 공간이 필요하기 때문에 별도의 변수영역을 추가로 확보하고, 데이터를 저장해 주소값을 연결시킨다.
- obj2 변수를 생성해 obj1을 할당한다. obj1의 값인 주소를 obj2에도 저장한다.
두 경우는 같은 주소를 바라보는 점에서 동일하지만
복사 이후 동작에서 큰 차이가 발생한다.
기본형 데이터의 값 변경
- b의 값 변경 시 데이터영역에 해당 값이 없으면 새로운 공간에 저장하고 새로운 주소를 b에 연결한다.
- 이제 a와 b는 다른 주소값값을 갖게된다.
참조형 데이터의 값 변경
- obj2 내부 속성 중 c의 값 변경 시 마찬가지로 데이터영역에 해당 값이 없으면 새로운 공간에 저장하고, 새로운 주소를 c가 있는 변수영역에 연결한다.
- c에 연결된 데이터 주소값은 달라졌지만 obj1 과 obj2는 여전히 같은 주소값을 가지고 있다.
엄밀히 말하면 기본형 데이터도 참조형 데이터도 모두 주소값을 통해 연결되는 참조형 데이터일 수밖에 없다. 하지만 참조형데이터는 변수영역을 한단계 더 거치게 된다는 차이가 있다.
참조형 데이터의 또 다른 값 변경
만약 obj2 자체를 처음부터 다른 객체로 만들어 할당한다면, 내부 변수영역이 별도로 마련되어야하므로 값 변경 시 obj1과 다른 데이터 주소를 갖게된다.
가변값인 참조형 데이터를 불변값으로 만들기
위의 내용으로 미루어보아 참조형 데이터의 가변적
특성은
데이터 자체가 아닌 내부 속성을 변경할때만 성립한다.
가변적이라는 것의 의미는 복제된 변수의 속성을 수정했음에도 원본 변수의 값이 같이 변하게 되는 것이다.
상황에따라 다를 수 있지만 만약 원본객체가 변하지 않아야하는 상황이라면
내부 속성값 변경이 일어날때마다 새로운 객체를 만들도록하는 도구를 활용해 불변성을 확보할 수 있다.
(ex. 객체 정보가 바뀌면 알림, 바뀌기 전과 후의 정보가 가시적으로 보여야 하는 기능 등이 필요할 수 있다.)
객체 내부 속성 변경 시 내부값을 모두 복사해 새로운 객체를 생성하는 함수를 만들어서 변경시 그 함수를 사용하자는 내부 규칙을 만들 수도 있지만,
애초에 그 규칙을 따르지 않고는 속성값을 변경할 수 없게 시스템적으로 제약을 거는 편이 안전하다.
이때 immutable.js
나 baobab.js
등의 라이브러리를 활용할 수 있다.
얕은 복사와 깊은 복사
얕은 복사
는 표면단계의 값만 복사하는 것이고
깊은 복사
는 내부의 모든 값을 하나하나 찾아 전부 복사하는 방법이다.
참조형 데이터에서 얕은복사를 하면 원본과 사본이 동일한 참조형 데이터 주소를 가리키게 된다.
따라서 데이터의 불변성 확보가 필요할때는 내부 속성을 모두 복사하는 동작을 재귀적으로 수행해야 깊은 복사를 수행할 수 있다.
깊은 복사를 수행하면 서로 다른 내부 변수영역을 참조하기때문에 속성값이 바뀌어도 서로에게 영향을 주지 않게 된다.
깊은 복사는 hasOwnProperty
메서드를 통해 할 수도 있고, JSON 문자열로 전환했다가 다시 JSON.parse(obj)
하는 방법도 있다.
undefined와 null의 차이
자바스크립트에서 없음을 나타내는 것은 undefined
와 null
이 있다.
undefined는 사용자가 명시적으로 지정할 수도 있지만, 자바스크립트 엔진이 자동으로 부여하는 경우도 있다.
자동부여가 되는 경우는 다음과 같다.
- 값을 대입하지 않은 변수에 접근할때
- 객체 내부의 존재하지 않는 속성에 접근하려고 할때
- return값이 없거나 호출되지 않는 함수를 실행할때
배열에서는 내부 값이 정의되지 않았을때 호출되면 결과가 조금 다르게 나온다.
속성이 없는 객체의 호출은 undefined가 되는 반면
비어있는 배열의 호출은 [empty]
로 표시되어, undefined 조차 할당되지 않음을 보여준다.
자바스크립트 엔진이 자동으로 반환하는 undefined는 값이 없음을 나타내어 비어있는 요소로 동작하지만 직접 입력한 undefined는 하나의 값이 된다.
이것은 여러 연산의 결과를 보면 알 수 있다.
forEach
map
filter
reduce
등을 수행했을때
비어있는 요소는 순회 대상에서 아예 제외된다.
하지만 undefined를 직접 할당한 경우 순회의 대상이 된다. undefined 그 자체로 하나의 값이 되는 것이다.
따라서 혼란을 피하기위해 명시적으로 undefined를 할당하는 것은 지양하는 것이 좋다.
만약 값이 비어있음을 명시적으로 나타내고자 할 때는 null을 사용해 엔진이 반환하는 undefined와 구분할 수 있도록 해야한다.
Leave a comment